Wie erhalte ich den vollständigen Pfad des laufenden Prozesses?


111

Ich habe eine Anwendung, die einige Einstellungen einer anderen Anwendung ändert (es ist eine einfache C # -Anwendung, die durch Doppelklick ausgeführt wird (kein Setup erforderlich)).

Nach dem Ändern der Einstellungen muss ich die andere Anwendung neu starten, damit sie die geänderten Einstellungen widerspiegelt.

Um dies zu tun, muss ich den laufenden Prozess beenden und den Prozess erneut starten. Das Problem ist jedoch, dass ich den Prozess nach dem Beenden nicht finden kann. (Grund ist, dass das System nicht weiß, wo sich die exe-Datei befindet.)

Gibt es eine Möglichkeit, den Pfad des laufenden Prozesses oder der exe herauszufinden, wenn dieser ausgeführt wird?

Ich möchte den Pfad nicht manuell angeben, dh wenn er ausgeführt wird, holen Sie sich den Pfad, beenden Sie den Prozess und starten Sie erneut. Andernfalls werde ich später darauf eingehen

Antworten:


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

Bei dieser API gibt es einen Haken: Wenn Sie diesen Code in einer 32-Bit-Anwendung ausführen, können Sie nicht auf 64-Bit-Anwendungspfade zugreifen. Daher müssen Sie Ihre App als 64-Bit-Anwendung kompilieren und ausführen ( Projekteigenschaften → Erstellen → Plattformziel → x64).


11
@ GAPS: Ich bin sicher, er meint: "Holen Sie sich Ihre Prozessinstanz, aber Sie bekommen sie hier."
Jeff Mercado

4
Es gibt ein Problem Der Zugriff wird online verweigert. string fullPath = process.Modules[0].FileName;Haben Sie bitte eine Idee?
Sami

7
Anstatt Platform Target in x64 zu ändern, habe ich Platform Target in Any geändert und die Option Prefer 32 bit
Prat

13
Nach meinen Messungen, Berufung process.Modules[0]ist 50 - mal langsamer als ein Anruf process.MainModule.
Luca Cremonesi

1
Gibt es eine Garantie dafür, dass das erste Modul das Hauptmodul ist?
Sam

112

Sie können WMI verwenden, um die Pfade abzurufen. Auf diese Weise können Sie den Pfad unabhängig von einer 32-Bit- oder 64-Bit-Anwendung abrufen. Hier ist ein Beispiel, das zeigt, wie Sie es bekommen können:

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

Beachten Sie, dass Sie auf die System.Management.dllAssembly verweisen und die verwenden müssenSystem.Management Namespace verwenden müssen.

Weitere Informationen zu den anderen Informationen, die Sie aus diesen Prozessen abrufen können, z. B. die Befehlszeile zum Starten des Programms ( CommandLine), finden Sie in der Win32_Process- Klasse und in WMI .NET .


1
Ihre Antwort ist fantastisch, aber meine aktuelle App ist klein ... Ich
denke

3
+1 vielleicht für diese Frage ist es ein Overkill, aber aufgrund der 32/64-Bit-Unabhängigkeit war diese Methode sehr praktisch, als ich 64-Bit-Prozessinformationen von einem laufenden 32-Bit-Prozess erhalten wollte .
Mike Fuchs

1
Im Gegensatz zur akzeptierten Antwort funktioniert dies auch in Terminalserverumgebungen. Gute Arbeit, hat mir sehr geholfen!
MC

1
Beachten Sie, dass die PathEigenschaft von mo["ExecutablePath"]ist nullfür einige Prozesse.
Sam

2
Falls Visual Studio sich über fehlende Referenzen für beschwert Process.GetProcesses()und results.Cast<>Sie auch eine using System.LinqDirektive hinzufügen müssen .
KibitzerCZ

26

Ich denke, Sie haben bereits das Prozessobjekt des laufenden Prozesses (z. B. von GetProcessesByName ()). Sie können dann den Namen der ausführbaren Datei mithilfe von abrufen

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

2
wenn nicht verwenden: var p = Process.GetCurrentProcess (); Zeichenfolge Dateiname = p.MainModule.FileName;
Andreas

3
"Ein 32-Bit-Prozess kann nicht auf Module eines 64-Bit-Prozesses zugreifen." Einschränkung gibt es leider auch hier.
Roland Pihlakas

18

Eine Lösung für:

  • Sowohl 32-Bit- als auch 64-Bit-Prozesse
  • Nur System.Diagnostics (kein System.Management)

Ich habe die Lösung von Russell Gantman verwendet und sie als Erweiterungsmethode umgeschrieben, die Sie wie folgt verwenden können:

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

Mit dieser Implementierung:

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 gibt BOOL zurück. Wir müssen es nicht mit 0 vergleichen. Pinvoke.net/default.aspx/kernel32.QueryFullProcessImageName
vik_78

8

Durch die Kombination der Antworten von Sanjeevakumar Hiremath und Jeff Mercado können Sie das Problem tatsächlich umgehen, wenn Sie das Symbol aus einem 64-Bit-Prozess in einem 32-Bit-Prozess abrufen.

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

Dies kann etwas langsam sein und funktioniert nicht bei jedem Prozess, dem ein "gültiges" Symbol fehlt.


Diese Verwendung könnte mit string query = "SELECT ExecutablePath, ProcessID FROM Win32_Process WHERE ProcessID = " + process.Id;... leicht verbessert werden, aber diese Methode ist immer noch recht langsam. Alle Ergebnisse zu erhalten und sie zwischenzuspeichern, wäre die beste Geschwindigkeitsverbesserung, wenn Sie den Pfad von mehr als einem Prozess erhalten
Thymine,

8

Hier ist eine zuverlässige Lösung, die sowohl mit 32-Bit- als auch mit 64-Bit- Anwendungen funktioniert .

Fügen Sie diese Referenzen hinzu:

using System.Diagnostics;

using System.Management;

Fügen Sie diese Methode Ihrem Projekt hinzu:

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

Verwenden Sie es jetzt so:

int RootProcessId = Process.GetCurrentProcess().Id;

GetProcessPath(RootProcessId);

Beachten Sie, dass diese Methode den entsprechenden ExecutePath zurückgibt, wenn Sie die ID des Prozesses kennen.

Extra für Interessierte:

Process.GetProcesses() 

... gibt Ihnen eine Reihe aller aktuell ausgeführten Prozesse und ...

Process.GetCurrentProcess()

... gibt Ihnen den aktuellen Prozess zusammen mit ihren Informationen, z. B. ID usw. und auch eingeschränkter Kontrolle, z. B. Töten usw. *


4

Sie können pInvoke und einen nativen Aufruf wie den folgenden verwenden. Dies scheint nicht die 32/64-Bit-Beschränkung zu haben (zumindest in meinen Tests)

Hier ist der Code

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

Versuchen:

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: Dies ist für die Ausführung von Assembly (die derzeit ausgeführt wird)
Sonal Satpute

Beeindruckend! Vielen Dank! Beste Lösung aller Zeiten, da sie auch unter FreeBSD funktioniert.
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

Für andere können Sie Folgendes verwenden, wenn Sie einen anderen Prozess derselben ausführbaren Datei suchen möchten:

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

Ich bin zu diesem Thread gekommen, als ich nach dem aktuellen Verzeichnis eines ausgeführten Prozesses gesucht habe. In .net 1.1 führte Microsoft Folgendes ein:

Directory.GetCurrentDirectory();

Scheint gut zu funktionieren (gibt aber nicht den Namen des Prozesses selbst zurück).


Dadurch wird nur das Verzeichnis zurückgegeben, in dem sich die ausführbare Datei unter bestimmten Umständen befindet. Sie können beispielsweise eine Befehlszeile öffnen, in ein beliebiges Verzeichnis wechseln und die ausführbare Datei ausführen, indem Sie einen vollständigen Pfad dazu angeben. GetCurrentDirectory () gibt das Verzeichnis zurück, aus dem Sie ausgeführt haben, und nicht das Verzeichnis der ausführbaren Datei. Vom Link : "Das aktuelle Verzeichnis unterscheidet sich vom ursprünglichen Verzeichnis, von dem aus der Prozess gestartet wurde."
Dave Ruske
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.