Làm thế nào để có được đường dẫn đầy đủ của quá trình đang chạy?


112

Tôi đang gặp một ứng dụng đang thay đổi một số cài đặt của ứng dụng khác (đó là một ứng dụng C # đơn giản chạy bằng cách nhấp đúp (không cần thiết lập)).

Sau khi thay đổi cài đặt, tôi cần khởi động lại ứng dụng khác để nó phản ánh các cài đặt đã thay đổi.

Vì vậy, để làm, tôi phải giết tiến trình đang chạy và bắt đầu lại quy trình, Nhưng vấn đề là sau khi giết, tôi không thể tìm thấy quy trình. (Lý do là hệ thống không biết tập tin exe ở đâu ..)

Có cách nào để tìm ra đường dẫn của quá trình đang chạy hoặc exe, nếu nó đang chạy?

Tôi không muốn cung cấp đường dẫn theo cách thủ công, tức là nếu nó đang chạy, hãy lấy đường dẫn, hãy giết quy trình và bắt đầu lại lần khác .... Tôi sẽ xử lý sau

Câu trả lời:


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ó một lỗi với API này, nếu bạn đang chạy mã này trong ứng dụng 32 bit, bạn sẽ không thể truy cập đường dẫn ứng dụng 64 bit, vì vậy bạn phải biên dịch và chạy ứng dụng của mình dưới dạng ứng dụng 64 bit ( Thuộc tính dự án → Xây dựng → Mục tiêu nền tảng → x64).


11
@GAPS: Tôi chắc chắn ý của anh ấy là "lấy phiên bản quy trình của bạn nhưng bạn lấy nó ở đây."
Jeff Mercado,

4
Nó gây ra sự cố Quyền truy cập bị từ chối trên đường dây string fullPath = process.Modules[0].FileName;Bất kỳ ý tưởng nào xin vui lòng?
Sami

7
Thay vì thay đổi Mục tiêu nền tảng thành x64, tôi đã thay đổi Mục tiêu nền tảng thành Bất kỳ và bỏ chọn tùy chọn Ưu tiên 32 bit
Prat

13
Theo số đo của tôi, gọi process.Modules[0]chậm hơn 50 lần so với gọi process.MainModule.
Luca Cremonesi

1
Có bất kỳ đảm bảo rằng mô-đun đầu tiên là mô-đun chính?
Sam

112

Những gì bạn có thể làm là sử dụng WMI để lấy các đường dẫn. Điều này sẽ cho phép bạn nhận được đường dẫn bất kể đó là ứng dụng 32 bit hay 64 bit. Dưới đây là một ví dụ minh họa cách bạn có thể lấy nó:

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

Lưu ý rằng bạn sẽ phải tham chiếu đến System.Management.dllassembly và sử dụng System.Managementkhông gian tên.

Để biết thêm thông tin về những thông tin khác mà bạn có thể lấy từ các quy trình này, chẳng hạn như dòng lệnh được sử dụng để khởi động chương trình ( CommandLine), hãy xem lớp Win32_ProcessWMI .NET để biết thêm thông tin.


1
Câu trả lời của bạn là tuyệt vời, nhưng ứng dụng hiện tại của tôi là một nhỏ ... Tôi ghi nhớ điều này
PawanS

3
+1 có lẽ đối với câu hỏi này là quá mức cần thiết, nhưng vì tính độc lập của 32 / 64bit, phương pháp này thực sự hữu ích khi tôi muốn lấy thông tin về quy trình 64 bit từ quy trình 32bit đang chạy .
Mike Fuchs

1
Không giống như Câu trả lời được chấp nhận, điều này cũng hoạt động trong Môi trường máy chủ đầu cuối. Công việc tốt, đã giúp tôi nhiều!
MC

1
Lưu ý rằng các Pathtài sản thiết lập từ mo["ExecutablePath"]nullđối với một số quy trình.
Sam

2
Trong trường hợp Visual Studio phàn nàn về việc thiếu tham chiếu cho Process.GetProcesses()results.Cast<>bạn cũng cần thêm using System.Linqchỉ thị.
kibitzerCZ

26

Tôi đoán bạn đã có đối tượng tiến trình của tiến trình đang chạy (ví dụ: bởi GetProcessesByName ()). Sau đó, bạn có thể lấy tên tệp thực thi bằng cách sử dụng

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

2
nếu không sử dụng: var p = Process.GetCurrentProcess (); string filename = p.MainModule.FileName;
Andreas

3
"Quy trình 32 bit không thể truy cập các mô-đun của quy trình 64 bit." không may là giới hạn ở đây.
Roland Pihlakas

18

Một giải pháp cho:

  • Cả quy trình 32 bit VÀ 64 bit
  • Chỉ System.Diagnostics (không có System.Management)

Tôi đã sử dụng giải pháp từ Russell Gantman và viết lại nó như một phương thức mở rộng mà bạn có thể sử dụng như sau:

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

Với cách triển khai này:

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 trả về BOOL. Chúng ta không cần phải so sánh nó với 0. pinvoke.net/default.aspx/kernel32.QueryFullProcessImageName
vik_78

8

Bằng cách kết hợp câu trả lời của Sanjeevakumar Hiremath và Jeff Mercado, bạn thực sự có thể giải quyết vấn đề khi truy xuất biểu tượng từ quy trình 64 bit trong quy trình 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 "";
        }
    }
}

Điều này có thể hơi chậm và không hoạt động trên mọi quy trình thiếu biểu tượng "hợp lệ".


Việc sử dụng này có thể được cải thiện một chút với string query = "SELECT ExecutablePath, ProcessID FROM Win32_Process WHERE ProcessID = " + process.Id;... nhưng phương pháp này vẫn khá chậm, việc nhận tất cả kết quả và 'lưu vào bộ nhớ đệm' chúng sẽ là cách cải thiện tốc độ tốt nhất, nếu bạn đang nhận được đường dẫn của nhiều hơn 1 quy trình
Thymine

8

Đây là một giải pháp đáng tin cậy hoạt động với cả ứng dụng 32bit64bit .

Thêm các tham chiếu sau:

sử dụng System.Diagnostics;

sử dụng System.Management;

Thêm phương pháp này vào dự án của bạn:

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

Bây giờ sử dụng nó như vậy:

int RootProcessId = Process.GetCurrentProcess().Id;

GetProcessPath(RootProcessId);

Lưu ý rằng nếu bạn biết id của tiến trình, thì phương thức này sẽ trả về ExecutePath tương ứng.

Thêm, cho những người quan tâm:

Process.GetProcesses() 

... sẽ cung cấp cho bạn một mảng của tất cả các quy trình hiện đang chạy và ...

Process.GetCurrentProcess()

... sẽ cung cấp cho bạn quy trình hiện tại, cùng với thông tin của họ, ví dụ: Id, v.v. và kiểm soát hạn chế, ví dụ: Kill, v.v. *


4

Bạn có thể sử dụng pInvoke và một cuộc gọi riêng như sau. Điều này dường như không có giới hạn 32/64 bit (ít nhất là trong thử nghiệm của tôi)

Đây là mã

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

Thử:

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: cái này dành cho Executing Assembly (cái này hiện đang chạy)
Sonal Satpute

Chà! Cảm ơn! Giải pháp tốt nhất từ ​​trước đến nay, vì hoạt động ngay cả trên 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

Đối với những người khác, nếu bạn muốn tìm một quy trình khác có cùng tệp thực thi, bạn có thể sử dụng:

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

Các Processlớp học có một thành viên StartInfomà bạn nên kiểm tra:


3
Điều này chỉ áp dụng cho các quy trình cục bộ đã được bắt đầu sử dụng Process.Start(), không áp dụng cho các quy trình hiện có.
Jeff Mercado

1
Chính xác. Nó đúng hơn là "cách bắt đầu quá trình" hơn là "cách quá trình này bắt đầu".
Robert Synoradzki

-3

Tôi đã đến chủ đề này trong khi tìm kiếm thư mục hiện tại của một quá trình đang thực thi. Trong .net 1.1 Microsoft đã giới thiệu:

Directory.GetCurrentDirectory();

Có vẻ hoạt động tốt (nhưng không trả về tên của chính quy trình).


Điều đó sẽ chỉ trả về thư mục chứa tệp thi hành trong một số trường hợp. Ví dụ, bạn có thể mở một dòng lệnh, thay đổi thành bất kỳ thư mục ngẫu nhiên nào và chạy tệp thực thi bằng cách chỉ định một đường dẫn đầy đủ đến nó; GetCurrentDirectory () sẽ trả về thư mục mà bạn đã thực thi từ đó thay vì thư mục của tệp thực thi. Từ liên kết : "Thư mục hiện tại khác với thư mục gốc, là thư mục mà từ đó quá trình được bắt đầu."
Dave Ruske
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.