How to Get Elevated Process Path in .Net

As you know retrieving process path in .Net is quite straightforward. For example if you have process instance someProcess you can get the process path by accessing someProcess.MainModule.FileName However for elevated processes the previous code snippet throws Win32Exception.

As you have probably guessed the reason is related to the bugs I mentioned in my previous post. In this case too the Process class is using PROCESS_QUERY_INFORMATION access right to open the desired process. The difference from the previous case is that in order to get the main module and process path the code really needs PROCESS_QUERY_INFORMATION so we cannot simply change it with PROCESS_QUERY_LIMITED_INFORMATION.

Fortunately Microsoft has introduced a new function for getting process path QueryFullProcessImageName which does work with PROCESS_QUERY_LIMITED_INFORMATION access right. To call the function we need to open the process ourselves and pass the handle.

private static string GetExecutablePath(Process Process)
{
    //If running on Vista or later use the new function
    if (Environment.OSVersion.Version.Major >= 6)
    {
        return GetExecutablePathAboveVista(Process.Id);
    }

    return Process.MainModule.FileName;
}

private static string GetExecutablePathAboveVista(int ProcessId)
{
    var buffer = new StringBuilder(1024);
    IntPtr hprocess = OpenProcess(ProcessAccessFlags.PROCESS_QUERY_LIMITED_INFORMATION,
                                  false, ProcessId);
    if (hprocess != IntPtr.Zero)
    {
        try
        {
            int size = buffer.Capacity;
            if (QueryFullProcessImageName(hprocess, 0, buffer, out size))
            {
                return buffer.ToString();
            }
        }
        finally
        {
            CloseHandle(hprocess);
        }
    }
    throw new Win32Exception(Marshal.GetLastWin32Error());
}

[DllImport("kernel32.dll")]
private static extern bool QueryFullProcessImageName(IntPtr hprocess, int dwFlags,
               StringBuilder lpExeName, out int size);
[DllImport("kernel32.dll")]
private static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess,
               bool bInheritHandle, int dwProcessId);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hHandle);

You can also turn the above code snippet into an extension method too. If you want Microsoft to add a new property for easily getting the process path vote for this suggestion at the connect website: Add Path property to Process class so that it works with elavated processes too.

Avatar
Giorgi Dalakishvili
World-Class Software Engineer

Related