Як отримати повний шлях запущеного процесу?


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: Я впевнений, що він має на увазі: "Отримайте ваш екземпляр процесу, проте ви отримаєте його тут".
Джефф Меркадо

4
Це створює проблеми в доступі заборонено в режимі онлайн string fullPath = process.Modules[0].FileName;Будь-яка ідея, будь ласка?
Самі

7
Замість того, щоб змінити ціль платформи на x64, я змінив ціль платформи на "Будь-який" і зняв галочку з 32-бітного варіанту "
Віддати

13
За моїми вимірами, дзвінки process.Modules[0]в 50 разів повільніше, ніж дзвінки process.MainModule.
Лука Кремонесі

1
Чи є гарантія того, що перший модуль є основним модулем?
Сем

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-бітного процесу .
Майк Фукс

1
На відміну від прийнятого відповіді, це також працює в середовищі Terminal Server Enviroments. Гарна робота, мені дуже допомогли!
MC

1
Зауважте, що Pathвластивість, встановлена ​​з, mo["ExecutablePath"]призначена nullдля деяких процесів.
Сем

2
У випадку, якщо Visual Studio скаржиться на відсутні дані Process.GetProcesses()та results.Cast<>вам також потрібно додати using System.Linqдирективу.
kibitzerCZ

26

Я думаю, у вас уже є об'єкт процесу запущеного процесу (наприклад, GetProcessByName ()). Потім ви можете отримати ім'я виконуваного файлу, використовуючи

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

2
якщо не використовується: var p = Process.GetCurrentProcess (); string filename = p.MainModule.FileName;
Андреас

3
"32-бітний процес не може отримати доступ до модулів 64-бітного процесу." На жаль, обмеження є і тут.
Roland Pihlakas

18

Рішення для:

  • Обидва 32-бітні І 64-бітні процеси
  • Тільки System.Diagnostics (без System.Management)

Я використав рішення від Рассела Гантмана і переписав його як метод розширення, який ви можете використовувати так:

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

Поєднуючи відповіді Саньєвакамура Хіремата та Джеффа Меркадо, ви насправді зможете подолати проблему під час отримання піктограми із 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);

Зауважте, що якщо ви знаєте ідентифікатор процесу, то цей метод поверне відповідний ExecutePath.

Додатково, для зацікавлених:

Process.GetProcesses() 

... дасть вам масив усіх поточно запущених процесів, і ...

Process.GetCurrentProcess()

... дасть вам поточний процес, разом із їх інформацією, наприклад, Id тощо, а також обмеженим контролем, наприклад, Kill тощо. *


4

Ви можете використовувати 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: це для виконання асамблеї (яка зараз працює)
Sonal Satpute

Оце Так! Дякую! Найкраще рішення коли-небудь, тому що працює навіть на FreeBSD.
бик

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(), а не існуючих.
Джефф Меркадо

1
Саме так. Це швидше "як почати процес", ніж "як цей процес почався".
Роберт Синорадзкі

-3

Я потрапив до цього потоку під час пошуку поточного каталогу виконавчого процесу. У .net 1.1 Microsoft представила:

Directory.GetCurrentDirectory();

Здається, працює добре (але не повертає назву самого процесу).


Це поверне лише каталог, в якому виконується виконуваний файл, за певних обставин. Наприклад, ви можете відкрити командний рядок, змінити будь-який випадковий каталог і запустити виконуваний файл, вказавши повний шлях до нього; GetCurrentDirectory () поверне каталог, який ви виконали, а не каталог виконуваного файлу. За посиланням : "Поточний каталог відрізняється від вихідного каталогу, який є тим, з якого був запущений процес."
Дейв Руске
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.