Come ottenere il percorso completo del processo in esecuzione?


112

Sto utilizzando un'applicazione che sta modificando alcune impostazioni di un'altra applicazione (è una semplice applicazione C # che viene eseguita facendo doppio clic (nessuna configurazione richiesta)).

Dopo aver modificato le impostazioni, è necessario riavviare l'altra applicazione in modo che rifletta le impostazioni modificate.

Quindi, per farlo, devo terminare il processo in esecuzione e ricominciare il processo, ma il problema è che dopo aver ucciso non sono in grado di trovare il processo. (Il motivo è che il sistema non sa dove si trova il file exe ..)

C'è un modo per scoprire il percorso di esecuzione del processo o exe, se è in esecuzione?

Non voglio dare il percorso manualmente, cioè se è in esecuzione prendi il percorso, uccidi il processo e ricomincia altrimenti ... lo gestirò più tardi

Risposte:


157
 using System.Diagnostics;
 var process = Process.GetCurrentProcess(); // Or whatever method you are using
 string fullPath = process.MainModule.FileName;
 //fullPath has the path to exe.

C'è un problema con questa API, se stai eseguendo questo codice in un'applicazione a 32 bit, non sarai in grado di accedere ai percorsi dell'applicazione a 64 bit, quindi dovresti compilare ed eseguire la tua app come applicazione a 64 bit ( Proprietà progetto → Crea → Destinazione piattaforma → x64).


11
@ GAPS: Sono sicuro che intende "ottieni la tua istanza di processo comunque la ottieni qui".
Jeff Mercado

4
Dà un problema Accesso negato in linea string fullPath = process.Modules[0].FileName;Qualche idea per favore?
Sami

7
Invece di cambiare Platform Target in x64, ho cambiato Platform Target in Any e deselezionato l' opzione Prefer 32 bit
Prat

13
Secondo le mie misurazioni, la chiamata process.Modules[0]è 50 volte più lenta della chiamata process.MainModule.
Luca Cremonesi

1
C'è qualche garanzia che il primo modulo sia il modulo principale?
Sam

112

Quello che puoi fare è usare WMI per ottenere i percorsi. Ciò ti consentirà di ottenere il percorso indipendentemente dal fatto che sia un'applicazione a 32 o 64 bit. Ecco un esempio che mostra come ottenerlo:

// include the namespace
using System.Management;

var wmiQueryString = "SELECT ProcessId, ExecutablePath, CommandLine FROM Win32_Process";
using (var searcher = new ManagementObjectSearcher(wmiQueryString))
using (var results = searcher.Get())
{
    var query = from p in Process.GetProcesses()
                join mo in results.Cast<ManagementObject>()
                on p.Id equals (int)(uint)mo["ProcessId"]
                select new
                {
                    Process = p,
                    Path = (string)mo["ExecutablePath"],
                    CommandLine = (string)mo["CommandLine"],
                };
    foreach (var item in query)
    {
        // Do what you want with the Process, Path, and CommandLine
    }
}

Tieni presente che dovrai fare riferimento System.Management.dllall'assembly e utilizzare lo System.Managementspazio dei nomi.

Per ulteriori informazioni su quali altre informazioni è possibile estrarre da questi processi, come la riga di comando utilizzata per avviare il programma ( CommandLine), vedere la classe Win32_Process e WMI .NET per ulteriori informazioni.


1
la tua risposta è fantastica, ma la mia attuale app è piccola ... lo tengo a mente
PawanS

3
+1 forse per questa domanda è eccessivo, ma a causa dell'indipendenza a 32/64 bit, questo metodo è stato davvero utile quando volevo ottenere informazioni sul processo a 64 bit da un processo a 32 bit in esecuzione .
Mike Fuchs

1
A differenza della risposta accettata, funziona anche in ambienti Terminal Server. Buon lavoro, mi ha aiutato molto!
MC

1
Notare che la Pathproprietà impostata da mo["ExecutablePath"]è nullper alcuni processi.
Sam

2
Nel caso in cui Visual Studio si lamenta dei riferimenti mancanti per Process.GetProcesses()e results.Cast<>devi anche aggiungere la using System.Linqdirettiva.
kibitzerCZ

26

Immagino che tu abbia già l'oggetto processo del processo in esecuzione (ad esempio da GetProcessesByName ()). È quindi possibile ottenere il nome del file eseguibile utilizzando

Process p;
string filename = p.MainModule.FileName;

2
in caso contrario, utilizzare: var p = Process.GetCurrentProcess (); stringa nomefile = p.MainModule.FileName;
Andreas

3
"I processi A 32 bit non possono accedere ai moduli di un processo a 64 bit." la limitazione è purtroppo anche qui.
Roland Pihlakas

18

Una soluzione per:

  • Processi sia a 32 bit che a 64 bit
  • Solo System.Diagnostics (no System.Management)

Ho usato la soluzione di Russell Gantman e l'ho riscritta come metodo di estensione che puoi usare in questo modo:

var process = Process.GetProcessesByName("explorer").First();
string path = process.GetMainModuleFileName();
// C:\Windows\explorer.exe

Con questa implementazione:

internal static class Extensions {
    [DllImport("Kernel32.dll")]
    private static extern bool QueryFullProcessImageName([In] IntPtr hProcess, [In] uint dwFlags, [Out] StringBuilder lpExeName, [In, Out] ref uint lpdwSize);

    public static string GetMainModuleFileName(this Process process, int buffer = 1024) {
        var fileNameBuilder = new StringBuilder(buffer);
        uint bufferLength = (uint)fileNameBuilder.Capacity + 1;
        return QueryFullProcessImageName(process.Handle, 0, fileNameBuilder, ref bufferLength) ?
            fileNameBuilder.ToString() :
            null;
    }
}

1
QueryFullProcessImageName restituisce BOOL. Non è necessario confrontarlo con 0. pinvoke.net/default.aspx/kernel32.QueryFullProcessImageName
vik_78

8

Combinando le risposte di Sanjeevakumar Hiremath e Jeff Mercado puoi effettivamente aggirare il problema quando recuperi l'icona da un processo a 64 bit in un processo a 32 bit.

using System;
using System.Management;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int processID = 6680;   // Change for the process you would like to use
            Process process = Process.GetProcessById(processID);
            string path = ProcessExecutablePath(process);
        }

        static private string ProcessExecutablePath(Process process)
        {
            try
            {
                return process.MainModule.FileName;
            }
            catch
            {
                string query = "SELECT ExecutablePath, ProcessID FROM Win32_Process";
                ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);

                foreach (ManagementObject item in searcher.Get())
                {
                    object id = item["ProcessID"];
                    object path = item["ExecutablePath"];

                    if (path != null && id.ToString() == process.Id.ToString())
                    {
                        return path.ToString();
                    }
                }
            }

            return "";
        }
    }
}

Potrebbe essere un po 'lento e non funziona su tutti i processi privi di un'icona "valida".


Questo utilizzo potrebbe essere leggermente migliorato con string query = "SELECT ExecutablePath, ProcessID FROM Win32_Process WHERE ProcessID = " + process.Id;... ma questo metodo è ancora piuttosto lento, ottenere tutti i risultati e "memorizzarli nella cache" sarebbe il miglior miglioramento della velocità, se stai ottenendo il percorso di più di 1 processo
Thymine

8

Ecco una soluzione affidabile che funziona con applicazioni sia a 32 bit che a 64 bit .

Aggiungi questi riferimenti:

using System.Diagnostics;

using System.Management;

Aggiungi questo metodo al tuo progetto:

public static string GetProcessPath(int processId)
{
    string MethodResult = "";
    try
    {
        string Query = "SELECT ExecutablePath FROM Win32_Process WHERE ProcessId = " + processId;

        using (ManagementObjectSearcher mos = new ManagementObjectSearcher(Query))
        {
            using (ManagementObjectCollection moc = mos.Get())
            {
                string ExecutablePath = (from mo in moc.Cast<ManagementObject>() select mo["ExecutablePath"]).First().ToString();

                MethodResult = ExecutablePath;

            }

        }

    }
    catch //(Exception ex)
    {
        //ex.HandleException();
    }
    return MethodResult;
}

Ora usalo in questo modo:

int RootProcessId = Process.GetCurrentProcess().Id;

GetProcessPath(RootProcessId);

Si noti che se si conosce l'id del processo, questo metodo restituirà il percorso ExecutePath corrispondente.

Extra, per chi è interessato:

Process.GetProcesses() 

... ti fornirà una serie di tutti i processi attualmente in esecuzione e ...

Process.GetCurrentProcess()

... ti fornirà il processo corrente, insieme alle loro informazioni ad es. Id, ecc. e anche un controllo limitato, ad es. Uccidi, ecc. *


4

È possibile utilizzare pInvoke e una chiamata nativa come la seguente. Questo non sembra avere la limitazione a 32/64 bit (almeno nei miei test)

Ecco il codice

using System.Runtime.InteropServices;

    [DllImport("Kernel32.dll")]
    static extern uint QueryFullProcessImageName(IntPtr hProcess, uint flags, StringBuilder text, out uint size);

    //Get the path to a process
    //proc = the process desired
    private string GetPathToApp (Process proc)
    {
        string pathToExe = string.Empty;

        if (null != proc)
        {
            uint nChars = 256;
            StringBuilder Buff = new StringBuilder((int)nChars);

            uint success = QueryFullProcessImageName(proc.Handle, 0, Buff, out nChars);

            if (0 != success)
            {
                pathToExe = Buff.ToString();
            }
            else
            {
                int error = Marshal.GetLastWin32Error();
                pathToExe = ("Error = " + error + " when calling GetProcessImageFileName");
            }
        }

        return pathToExe;
    }

1

Provare:

using System.Diagnostics;

ProcessModuleCollection modules = Process.GetCurrentProcess().Modules;
string processpathfilename;
string processmodulename;
if (modules.Count > 0) {
    processpathfilename = modules[0].FileName;
    processmodulename= modules[0].ModuleName;
} else {
    throw new ExecutionEngineException("Something critical occurred with the running process.");
}

0
private void Test_Click(object sender, System.EventArgs e){
   string path;
   path = System.IO.Path.GetDirectoryName( 
      System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase );
    Console.WriiteLine( path );  
}

@ GAPS: questo è per Executing Assembly (il che è attualmente in esecuzione)
Sonal Satpute

Wow! Grazie! La migliore soluzione in assoluto, perché funziona anche su FreeBSD.
biv

0
using System;
using System.Diagnostics;

class Program
{
    public static void printAllprocesses()
    {
        Process[] processlist = Process.GetProcesses();

        foreach (Process process in processlist)
        {
            try
            {
                String fileName = process.MainModule.FileName;
                String processName = process.ProcessName;

                Console.WriteLine("processName : {0},  fileName : {1}", processName, fileName);
            }catch(Exception e)
            {
                /* You will get access denied exception for system processes, We are skiping the system processes here */
            }

        }
    }

    static void Main()
    {
        printAllprocesses();
    }

}

0

Per altri, se vuoi trovare un altro processo dello stesso eseguibile, puoi usare:

public bool tryFindAnotherInstance(out Process process) {
    Process thisProcess = Process.GetCurrentProcess();
    string thisFilename = thisProcess.MainModule.FileName;
    int thisPId = thisProcess.Id;
    foreach (Process p in Process.GetProcesses())
    {
        try
        {
            if (p.MainModule.FileName == thisFilename && thisPId != p.Id)
            {
                process = p;
                return true;
            }
        }
        catch (Exception)
        {

        }
    }
    process = default;
    return false;
}


-3

Sono arrivato a questo thread mentre cercavo la directory corrente di un processo in esecuzione. In .net 1.1 Microsoft ha introdotto:

Directory.GetCurrentDirectory();

Sembra funzionare bene (ma non restituisce il nome del processo stesso).


Ciò restituirà solo la directory in cui si trova l'eseguibile in alcune circostanze. È possibile, ad esempio, aprire una riga di comando, passare a qualsiasi directory casuale ed eseguire l'eseguibile specificando un percorso completo; GetCurrentDirectory () restituirà la directory da cui hai eseguito l'esecuzione anziché la directory dell'eseguibile. Dal collegamento : "La directory corrente è distinta dalla directory originale, che è quella da cui è stato avviato il processo."
Dave Ruske
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.