Search code examples
c#processpathexecutableprocessstartinfo

C#: How to get the executable path that Process.Start will use when given a file with no path?


The System.Diagnostics.Process.Start() method accepts a ProcessStartInfo class instance initialized with an executable with no path, such as Notepad.exe. After the process starts one can find the full path it used, such as C:\Windows\SysWOW64\notepad.exe. This is perfect, except when you would like to know the full path without actually launching the program. In my case, I'd like to get the icon from the executable ahead of time.

This is similar to the behavior of the windows "where" command, for example:

C:>where notepad.exe
C:>\Windows\System32\notepad.exe
C:>\Windows\notepad.exe

The first response C:\Windows\System32\notepad.exe is essentially the same as that used by "Process".


Solution

  • The order in which paths are searched is actually registry-dependent, so simply enumerating through the PATH environment variable is not guaranteed to produce the expected result, particularly where there is a file of the expected name in the current working directory. To reliably get the executable path, you will want to call the SearchPath Win32 function in Kernel32.

    There is no framework .NET function that exposes SearchPath, but the function can be invoked directly through P/Invoke.

    The following sample program illustrates the usage of this function. If notepad.exe exists in the system search paths, per the system configuration, it will print the path; if it does not exist, it will print "File not found" instead.

    using System;
    using System.Text;
    using System.Runtime.InteropServices;
    
    class Program
    {
        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern uint SearchPath(string lpPath,
             string lpFileName,
             string lpExtension,
             int nBufferLength,
             [MarshalAs ( UnmanagedType.LPTStr )]
                 StringBuilder lpBuffer,
             out IntPtr lpFilePart);
        const int MAX_PATH = 260;
        public static void Main()
        {
            StringBuilder sb = new StringBuilder(MAX_PATH);
            IntPtr discard;
            var nn = SearchPath(null, "notepad.exe", null, sb.Capacity, sb, out discard);
            if (nn == 0)
            {
                var error = Marshal.GetLastWin32Error();
                // ERROR_FILE_NOT_FOUND = 2
                if (error == 2) Console.WriteLine("No file found.");
                else
                    throw new System.ComponentModel.Win32Exception(error);
            }
            else
                Console.WriteLine(sb.ToString());
        }
    }