วิธีการรับเส้นทางการทำงานที่สมบูรณ์?


112

ฉันมีแอพพลิเคชั่นที่เปลี่ยนการตั้งค่าบางอย่างของแอพพลิเคชั่นอื่น ๆ (เป็นแอพพลิเคชั่น C # ธรรมดาที่รันโดยดับเบิลคลิก (ไม่จำเป็นต้องตั้งค่า))

หลังจากเปลี่ยนการตั้งค่าแล้วฉันต้องรีสตาร์ทแอปพลิเคชันอื่นเพื่อให้แสดงการตั้งค่าที่เปลี่ยนแปลงไป

ดังนั้นฉันต้องฆ่ากระบวนการที่กำลังทำงานอยู่และเริ่มกระบวนการอีกครั้ง แต่ปัญหาคือหลังจากฆ่าฉันไม่พบกระบวนการ (เหตุผลคือระบบไม่ทราบว่าไฟล์ exe อยู่ที่ไหน .. )

มีวิธีใดในการค้นหาเส้นทางของกระบวนการทำงานหรือ exe หากกำลังทำงานอยู่

ฉันไม่ต้องการให้เส้นทางด้วยตนเองคือถ้ามันกำลังทำงานรับเส้นทางให้ฆ่าการประมวลผลและเริ่มต้นใหม่อีกครั้ง .... ฉันจะจัดการในภายหลัง

คำตอบ:


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.

มีสิ่งหนึ่งที่จับได้กับ API นี้หากคุณใช้รหัสนี้ในแอปพลิเคชัน 32 บิตคุณจะไม่สามารถเข้าถึงเส้นทางแอปพลิเคชัน 64 บิตได้ดังนั้นคุณจะต้องรวบรวมและเรียกใช้แอปของคุณเป็นแอปพลิเคชัน 64 บิต ( คุณสมบัติโครงการ→สร้าง→เป้าหมายแพลตฟอร์ม→ x64)


11
@GAPS: ฉันแน่ใจว่าเขาหมายถึง "รับอินสแตนซ์กระบวนการของคุณ แต่คุณจะได้รับที่นี่"
Jeff Mercado

4
มันทำให้ปัญหาการเข้าถึงถูกปฏิเสธทางออนไลน์string fullPath = process.Modules[0].FileName;โปรดคิดอย่างไร
Sami

7
แทนที่จะเปลี่ยน Platform Target เป็น x64 ฉันเปลี่ยน Platform Target เป็น Any และยกเลิกการเลือกตัวเลือกPrefer 32 bit
Prat

13
และจากการวัดของฉันโทรprocess.Modules[0]เป็น50 ครั้งช้าprocess.MainModuleกว่าการเรียก
Luca Cremonesi

1
มีการรับประกันว่าโมดูลแรกเป็นโมดูลหลักหรือไม่?
Sam

112

สิ่งที่คุณทำได้คือใช้ WMI เพื่อรับเส้นทาง วิธีนี้จะช่วยให้คุณได้รับเส้นทางไม่ว่าจะเป็นแอปพลิเคชัน 32 บิตหรือ 64 บิต นี่คือตัวอย่างที่แสดงให้เห็นว่าคุณจะได้รับมันได้อย่างไร:

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

โปรดทราบว่าคุณจะต้องอ้างอิงSystem.Management.dllแอสเซมบลีและใช้System.Managementเนมสเปซ

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับข้อมูลอื่น ๆ ที่คุณสามารถดึงออกมาจากกระบวนการเหล่านี้เช่นบรรทัดคำสั่งที่ใช้ในการเริ่มโปรแกรม ( CommandLine) โปรดดูที่คลาสWin32_ProcessและWMI .NETสำหรับข้อมูลเพิ่มเติม


1
คำตอบของคุณยอดเยี่ยม แต่แอปปัจจุบันของฉันมีขนาดเล็ก ... ฉันจำสิ่งนี้ไว้
PawanS

3
+1 บางทีสำหรับคำถามนี้มันเกินความจำเป็น แต่เนื่องจากความเป็นอิสระ 32/64 บิตวิธีนี้จึงมีประโยชน์มากเมื่อฉันต้องการรับข้อมูลกระบวนการ 64 บิตจากกระบวนการ 32 บิตที่ทำงานอยู่
Mike Fuchs

1
ซึ่งแตกต่างจากคำตอบที่ได้รับการยอมรับสิ่งนี้ยังใช้ได้ในสภาพแวดล้อมเซิร์ฟเวอร์เทอร์มินัล ทำได้ดีมากช่วยฉันได้มาก!
MC

1
โปรดสังเกตว่าPathคุณสมบัติที่ตั้งค่าจากmo["ExecutablePath"]เป็นnullสำหรับบางกระบวนการ
แซม

2
ในกรณีที่ Visual Studio ร้องเรียนเกี่ยวกับการอ้างอิงที่ขาดหายไปProcess.GetProcesses()และresults.Cast<>คุณต้องเพิ่มusing System.Linqคำสั่งด้วย
kibitzerCZ

26

ฉันเดาว่าคุณมีวัตถุกระบวนการของกระบวนการทำงานอยู่แล้ว (เช่นโดย GetProcessesByName ()) จากนั้นคุณสามารถรับชื่อไฟล์ปฏิบัติการได้โดยใช้

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

2
ถ้าไม่ใช้: var p = Process.GetCurrentProcess (); ชื่อไฟล์สตริง = p.MainModule.FileName;
Andreas

3
"กระบวนการ 32 บิตไม่สามารถเข้าถึงโมดูลของกระบวนการ 64 บิต" ก็มีข้อ จำกัด เช่นกัน
Roland Pihlakas

19

ทางออกสำหรับ:

  • ทั้งกระบวนการ 32 บิตและ 64 บิต
  • System.Diagnostics เท่านั้น (ไม่มี System.Management)

ฉันใช้วิธีแก้ปัญหาจาก Russell Gantmanและเขียนใหม่เป็นวิธีการขยายที่คุณสามารถใช้ได้ดังนี้:

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

ด้วยการใช้งานนี้:

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 ส่งคืน BOOL เราไม่จำเป็นต้องเปรียบเทียบกับ 0. pinvoke.net/default.aspx/kernel32.QueryFullProcessImageName
vik_78

8

ด้วยการรวมคำตอบของ Sanjeevakumar Hiremath และ Jeff Mercado เข้าด้วยกันคุณจะสามารถแก้ไขปัญหาได้จริงเมื่อดึงไอคอนจากกระบวนการ 64 บิตในกระบวนการ 32 บิต

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

ซึ่งอาจจะช้าไปหน่อยและใช้ไม่ได้กับทุกกระบวนการที่ไม่มีไอคอน "ถูกต้อง"


การใช้งานนี้อาจได้รับการปรับปรุงเล็กน้อยด้วยstring query = "SELECT ExecutablePath, ProcessID FROM Win32_Process WHERE ProcessID = " + process.Id;... แต่วิธีนี้ยังค่อนข้างช้าการได้รับผลลัพธ์ทั้งหมดและ 'การแคช' จะเป็นการปรับปรุงความเร็วที่ดีที่สุดหากคุณได้รับเส้นทางมากกว่า 1 กระบวนการ
Thymine

8

นี่คือโซลูชันที่เชื่อถือได้ซึ่งใช้ได้กับทั้ง32 บิตและ64 บิตแอปพลิเคชัน

เพิ่มการอ้างอิงเหล่านี้:

ใช้ System.Diagnostics;

ใช้ System.Management;

เพิ่มวิธีนี้ในโครงการของคุณ:

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

ตอนนี้ใช้มันดังนี้:

int RootProcessId = Process.GetCurrentProcess().Id;

GetProcessPath(RootProcessId);

สังเกตว่าถ้าคุณรู้ id ของกระบวนการวิธีนี้จะส่งคืน ExecutePath ที่เกี่ยวข้อง

พิเศษสำหรับผู้ที่สนใจ:

Process.GetProcesses() 

... จะให้อาร์เรย์ของกระบวนการที่กำลังทำงานอยู่ทั้งหมดและ ...

Process.GetCurrentProcess()

... จะให้กระบวนการปัจจุบันพร้อมกับข้อมูลของพวกเขาเช่น Id เป็นต้นและการควบคุมที่ จำกัด เช่น Kill เป็นต้น *


5

คุณสามารถใช้ pInvoke และการโทรแบบเนทีฟได้ดังต่อไปนี้ ดูเหมือนจะไม่มีข้อ จำกัด 32/64 บิต (อย่างน้อยก็ในการทดสอบของฉัน)

นี่คือรหัส

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

ลอง:

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: นี่คือสำหรับ Executing Assembly (ซึ่งกำลังทำงานอยู่)
Sonal Satpute

ว้าว! ขอบคุณ! ทางออกที่ดีที่สุดเพราะใช้งานได้แม้ใน 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

สำหรับผู้อื่นหากคุณต้องการค้นหากระบวนการอื่นของปฏิบัติการเดียวกันคุณสามารถใช้:

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

Processชั้นจะมีสมาชิกคนหนึ่งStartInfoที่คุณควรตรวจสอบ:


3
ใช้เฉพาะกับกระบวนการโลคัลที่เริ่มต้นโดยใช้Process.Start()ไม่ใช่กระบวนการที่มีอยู่
Jeff Mercado

1
ตรง เป็น "วิธีเริ่มต้นกระบวนการ" มากกว่า "กระบวนการนี้เริ่มต้นอย่างไร"
Robert Synoradzki

-3

ฉันไปที่เธรดนี้ในขณะที่ค้นหาไดเร็กทอรีปัจจุบันของกระบวนการดำเนินการ ใน. net 1.1 Microsoft เปิดตัว:

Directory.GetCurrentDirectory();

ดูเหมือนจะทำงานได้ดี (แต่ไม่คืนชื่อของกระบวนการเอง)


นั่นจะส่งคืนไดเร็กทอรีที่ไฟล์ปฏิบัติการอยู่ในบางสถานการณ์เท่านั้น ตัวอย่างเช่นคุณสามารถเปิดบรรทัดคำสั่งเปลี่ยนเป็นไดเร็กทอรีแบบสุ่มใดก็ได้และเรียกใช้ไฟล์ปฏิบัติการโดยระบุพา ธ แบบเต็มไป GetCurrentDirectory () จะส่งคืนไดเร็กทอรีที่คุณเรียกใช้งานแทนที่จะเป็นไดเร็กทอรีของไฟล์ปฏิบัติการ จากลิงก์ : "ไดเร็กทอรีปัจจุบันแตกต่างจากไดเร็กทอรีดั้งเดิมซึ่งเป็นไดเร็กทอรีที่เริ่มต้นกระบวนการ"
Dave Ruske
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.