Como obter o caminho completo do processo em execução?


112

Estou tendo um aplicativo que está alterando algumas configurações de outro aplicativo (é um aplicativo C # simples que é executado com um clique duplo (nenhuma configuração necessária)).

Depois de alterar as configurações, preciso reiniciar o outro aplicativo para que reflita as configurações alteradas.

Para fazer isso, preciso encerrar o processo em execução e reiniciá-lo, mas o problema é que, após matar, não consigo encontrar o processo. (A razão é que o sistema não sabe onde está o arquivo exe ..)

Existe alguma maneira de descobrir o caminho do processo em execução ou exe, se estiver em execução?

Não quero dar o caminho manualmente, ou seja, se estiver rodando pegue o caminho, mate o processo e comece de novo senão .... tratarei mais tarde

Respostas:


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.

Existe um problema com esta API, se você estiver executando este código em um aplicativo de 32 bits, você não será capaz de acessar os caminhos de aplicativos de 64 bits, então você terá que compilar e executar seu aplicativo como um aplicativo de 64 bits ( Propriedades do projeto → Compilar → Destino da plataforma → x64).


11
@GAPS: Tenho certeza de que ele quis dizer "obtenha sua instância de processo da maneira que for obtida aqui".
Jeff Mercado,

4
Dá problema Acesso negado online string fullPath = process.Modules[0].FileName;Alguma ideia, por favor?
Sami

7
Em vez de alterar Platform Target para x64, alterei Platform Target para Any e desmarquei a opção Prefer 32 bit
Prat,

13
De acordo com minhas medições, ligar process.Modules[0]é 50 vezes mais lento do que ligar process.MainModule.
Luca Cremonesi

1
Existe alguma garantia de que o primeiro módulo é o módulo principal?
Sam

112

O que você pode fazer é usar o WMI para obter os caminhos. Isso permitirá que você obtenha o caminho, independentemente de ser um aplicativo de 32 ou 64 bits. Aqui está um exemplo que demonstra como você pode obtê-lo:

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

Observe que você terá que fazer referência ao System.Management.dllassembly e usar o System.Managementnamespace.

Para obter mais informações sobre quais outras informações você pode obter desses processos, como a linha de comando usada para iniciar o programa ( CommandLine), consulte a classe Win32_Process e WMI .NET para obter mais informações.


1
sua resposta é incrível, mas meu aplicativo atual é pequeno ... Tenho isso em mente
PawanS

3
+1 talvez para esta questão seja um exagero, mas por causa da independência de 32/64 bits, este método foi realmente útil quando eu queria obter informações do processo de 64 bits de um processo de 32 bits em execução .
Mike Fuchs,

1
Ao contrário da Resposta aceita, isso também funciona em ambientes de Terminal Server. Bom trabalho, me ajudou muito!
MC de

1
Observe que o Pathconjunto de propriedades de mo["ExecutablePath"]é nullpara alguns processos.
Sam

2
Caso o Visual Studio reclame sobre a falta de referências para Process.GetProcesses()e results.Cast<>você também precise adicionar uma using System.Linqdiretiva.
kibitzerCZ

26

Eu acho que você já tem o objeto de processo do processo em execução (por exemplo, por GetProcessesByName ()). Você pode obter o nome do arquivo executável usando

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

2
se não, use: var p = Process.GetCurrentProcess (); string filename = p.Module Principal.NomeArquivo;
Andreas

3
Os "processos de 32 bits não podem acessar módulos de um processo de 64 bits." limitação está infelizmente aqui também.
Roland Pihlakas,

18

Uma solução para:

  • Processos de 32 bits E de 64 bits
  • Apenas System.Diagnostics (sem System.Management)

Usei a solução de Russell Gantman e a reescrevi como um método de extensão que você pode usar assim:

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

Com esta implementação:

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 retorna BOOL. Não precisamos compará-lo com 0. pinvoke.net/default.aspx/kernel32.QueryFullProcessImageName
vik_78

8

Ao combinar as respostas de Sanjeevakumar Hiremath e Jeff Mercado, você pode, de certa forma, contornar o problema ao recuperar o ícone de um processo de 64 bits em um processo de 32 bits.

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

Isso pode ser um pouco lento e não funciona em todos os processos que não possuem um ícone "válido".


Este uso pode ser ligeiramente melhorado com string query = "SELECT ExecutablePath, ProcessID FROM Win32_Process WHERE ProcessID = " + process.Id;... mas este método ainda é bastante lento, obter todos os resultados e 'armazená-los em cache' seria a melhor melhoria de velocidade, se você estiver obtendo o caminho de mais de 1 processo
Thymine

8

Aqui está uma solução confiável que funciona com aplicativos de 32 bits e 64 bits .

Adicione essas referências:

using System.Diagnostics;

using System.Management;

Adicione este método ao seu projeto:

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

Agora use-o assim:

int RootProcessId = Process.GetCurrentProcess().Id;

GetProcessPath(RootProcessId);

Observe que se você souber a id do processo, este método retornará o ExecutePath correspondente.

Extra, para os interessados:

Process.GetProcesses() 

... lhe dará uma série de todos os processos em execução no momento, e ...

Process.GetCurrentProcess()

... fornecerá o processo atual, junto com suas informações, por exemplo, Id, etc. e também controle limitado, por exemplo, Kill, etc. *


4

Você pode usar pInvoke e uma chamada nativa, como a seguir. Isso não parece ter a limitação de 32/64 bits (pelo menos em meus testes)

Aqui está o código

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

Experimentar:

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: isto é para Executing Assembly (o que está atualmente em execução)
Sonal Satpute

Uau! Obrigado! Melhor Solução de todas, pois funciona até no 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

Para outros, se quiser encontrar outro processo do mesmo executável, você pode usar:

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

Cheguei a este tópico enquanto procurava o diretório atual de um processo em execução. Em .net 1.1 a Microsoft introduziu:

Directory.GetCurrentDirectory();

Parece funcionar bem (mas não retorna o nome do próprio processo).


Isso retornará apenas o diretório em que o executável está localizado em algumas circunstâncias. Você pode, por exemplo, abrir uma linha de comando, mudar para qualquer diretório aleatório e executar o executável especificando um caminho completo para ele; GetCurrentDirectory () retornará o diretório a partir do qual você executou em vez do diretório do executável. Do link : "O diretório atual é diferente do diretório original, que é aquele a partir do qual o processo foi iniciado."
Dave Ruske
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.