Jak uzyskać pełną ścieżkę przebiegu procesu?


112

Mam aplikację, która zmienia niektóre ustawienia innej aplikacji (jest to prosta aplikacja C # uruchamiana przez dwukrotne kliknięcie (nie jest wymagana konfiguracja)).

Po zmianie ustawień muszę zrestartować inną aplikację, aby odzwierciedlała zmienione ustawienia.

Aby to zrobić, muszę zabić działający proces i rozpocząć go ponownie, ale problem polega na tym, że po zabiciu nie jestem w stanie znaleźć procesu. (Przyczyną jest to, że system nie wie, gdzie znajduje się plik exe ...)

Czy jest jakiś sposób, aby znaleźć ścieżkę do uruchomionego procesu lub exe, jeśli jest uruchomiony?

Nie chcę podawać ścieżki ręcznie, tj. Jeśli jest uruchomiona, pobierz ścieżkę, zakończ proces i zacznij od nowa ... Zajmę się później

Odpowiedzi:


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.

Jest jeden haczyk z tym API, jeśli uruchamiasz ten kod w aplikacji 32-bitowej, nie będziesz mieć dostępu do ścieżek aplikacji 64-bitowych, więc będziesz musiał skompilować i uruchomić aplikację jako aplikację 64-bitową ( Właściwości projektu → Kompilacja → Cel platformy → x64).


11
@GAPS: Jestem pewien, że ma na myśli „pobierz instancję procesu, jakkolwiek ją tu dostaniesz”.
Jeff Mercado,

4
To powoduje problem Odmowa dostępu przez Internet string fullPath = process.Modules[0].FileName;Masz jakiś pomysł?
Sami

7
Zamiast zmienić Platform Target na x64, zmieniłem Platform Target na Any i odznaczyłem opcję Preferuj 32 bit
Prat

13
Według moich pomiarów dzwonienie process.Modules[0]jest 50 razy wolniejsze niż dzwonienie process.MainModule.
Luca Cremonesi

1
Czy jest jakaś gwarancja, że ​​pierwszy moduł jest głównym modułem?
Sam

112

Możesz użyć WMI, aby uzyskać ścieżki. Umożliwi to uzyskanie ścieżki niezależnie od tego, czy jest to aplikacja 32-bitowa, czy 64-bitowa. Oto przykład pokazujący, jak możesz to uzyskać:

// 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
    }
}

Zauważ, że będziesz musiał odwołać się do System.Management.dllzestawu i użyć System.Managementprzestrzeni nazw.

Aby uzyskać więcej informacji na temat innych informacji, które można pobrać z tych procesów, takich jak wiersz poleceń używany do uruchamiania programu ( CommandLine), zobacz klasę Win32_Process i WMI .NET, aby uzyskać więcej informacji.


1
Twoja odpowiedź jest niesamowita, ale moja obecna aplikacja jest niewielka ... Pamiętam o tym
PawanS

3
+1 być może dla tego pytania to przesada, ale ze względu na niezależność 32/64-bitową ta metoda okazała się bardzo przydatna, gdy chciałem uzyskać informacje o procesach 64-bitowych z działającego procesu 32-bitowego .
Mike Fuchs

1
W przeciwieństwie do zaakceptowanej odpowiedzi działa to również w środowiskach serwera terminali. Dobra robota, bardzo mi pomogła!
MC

1
Zwróć uwagę, że Pathwłaściwość ustawiona z mo["ExecutablePath"]jest nullprzeznaczona dla niektórych procesów.
Sam

2
W przypadku, gdy Visual Studio narzeka na brakujące odwołania dla Process.GetProcesses()i results.Cast<>musisz również dodać using System.Linqdyrektywę.
kibitzerCZ

26

Domyślam się, że masz już obiekt procesu działającego procesu (np. Przez GetProcessesByName ()). Następnie możesz uzyskać nazwę pliku wykonywalnego za pomocą

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

2
jeśli nie, użyj: var p = Process.GetCurrentProcess (); string filename = p.MainModule.FileName;
Andreas

3
„Procesy 32-bitowe nie mogą uzyskać dostępu do modułów procesu 64-bitowego”. tutaj niestety też jest ograniczenie.
Roland Pihlakas

18

Rozwiązanie dla:

  • Zarówno procesy 32-bitowe, jak i 64-bitowe
  • Tylko System.Diagnostics (bez System.Management)

Użyłem rozwiązania Russella Gantmana i przepisałem je jako metodę rozszerzenia, której możesz użyć w następujący sposób:

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

Dzięki tej implementacji:

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 zwraca wartość typu BOOL. Nie trzeba porównać ją z 0. pinvoke.net/default.aspx/kernel32.QueryFullProcessImageName
vik_78

8

Łącząc odpowiedzi Sanjeevakumar Hiremath i Jeffa Mercado, możesz w pewien sposób obejść problem podczas pobierania ikony z procesu 64-bitowego w procesie 32-bitowym.

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 "";
        }
    }
}

Może to być trochę powolne i nie działa na każdym procesie, który nie posiada „prawidłowej” ikony.


To użycie można nieco poprawić za pomocą string query = "SELECT ExecutablePath, ProcessID FROM Win32_Process WHERE ProcessID = " + process.Id;... ale ta metoda jest nadal dość powolna, uzyskanie wszystkich wyników i `` buforowanie '' ich byłoby najlepszą poprawą szybkości, jeśli otrzymujesz ścieżkę więcej niż jednego procesu
Thymine

8

Oto niezawodne rozwiązanie, które działa zarówno z 32 - bitowym, jak i 64 - bitowym aplikacjami .

Dodaj te odniesienia:

using System.Diagnostics;

using System.Management;

Dodaj tę metodę do swojego projektu:

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;
}

Teraz użyj tego w ten sposób:

int RootProcessId = Process.GetCurrentProcess().Id;

GetProcessPath(RootProcessId);

Zwróć uwagę, że jeśli znasz identyfikator procesu, ta metoda zwróci odpowiednią ścieżkę ExecutePath.

Dodatkowo dla zainteresowanych:

Process.GetProcesses() 

... da ci tablicę wszystkich aktualnie uruchomionych procesów i ...

Process.GetCurrentProcess()

... poda bieżący proces wraz z ich informacjami, np. Id, itp., a także ograniczoną kontrolę, np. Kill, itp. *


4

Możesz użyć pInvoke i wywołania natywnego, takiego jak poniższe. Wygląda na to, że nie ma ograniczenia 32/64-bitowego (przynajmniej w moich testach)

Oto kod

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

Próbować:

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: to jest dla wykonywania zestawu (który jest obecnie uruchomiony)
Sonal Satpute

Łał! Dzięki! Najlepsze rozwiązanie w historii, ponieważ działa nawet na 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

W przypadku innych, jeśli chcesz znaleźć inny proces tego samego pliku wykonywalnego, możesz użyć:

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

Dotarłem do tego wątku, szukając bieżącego katalogu wykonywanego procesu. W .net 1.1 Microsoft wprowadził:

Directory.GetCurrentDirectory();

Wydaje się działać dobrze (ale nie zwraca nazwy samego procesu).


To zwróci tylko katalog, w którym znajduje się plik wykonywalny w pewnych okolicznościach. Możesz na przykład otworzyć wiersz poleceń, przejść do dowolnego losowego katalogu i uruchomić plik wykonywalny, podając pełną ścieżkę do niego; GetCurrentDirectory () zwróci katalog, z którego wykonałeś operację, a nie katalog pliku wykonywalnego. Od linku : „Bieżący katalog różni się od oryginalnego katalogu, czyli tego, z którego został uruchomiony proces”.
Dave Ruske,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.