Как получить полный путь к запущенному процессу?


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;Есть идея, пожалуйста?
— Sami

7
Вместо изменения Platform Target на x64 я изменил Platform Target на Any и снял отметку с опции Prefer 32 bit
— Prat

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
В отличие от принятого ответа, это также работает в среде терминального сервера. Молодцы, мне очень помогли!
— 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 (); строка filename = p.MainModule.FileName;
— Андреас

3
«32-битные процессы не могут получить доступ к модулям 64-битного процесса». ограничение, к сожалению, здесь тоже есть.
— Роланд Пихлакас

19

Решение для:

  • И 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;... но этот метод все еще довольно медленный, получение всех результатов и их `` кеширование '' было бы лучшим улучшением скорости, если вы получаете путь из более чем одного процесса
— 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 и т. д. *


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 03

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 () вернет каталог, из которого вы запустили, а не каталог исполняемого файла. Из ссылки : «Текущий каталог отличается от исходного каталога, из которого был запущен процесс».
— Dave Ruske
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.