Check if an executable exists in the Windows path

后端 未结 8 1743
栀梦
栀梦 2020-11-29 06:39

If I run a process with ShellExecute (or in .net with System.Diagnostics.Process.Start()) the filename process to start doesn\'t need to be a full

8条回答
  •  生来不讨喜
    2020-11-29 06:46

    I combined the answers by @Ron and @Hans Passant to create a class that checks for the file path in both App Path registry key, and in PATH by calling PathFindOnPath. It also allows to omit the file extension. In such cases, it probes for several possible "executable" file extensions from PATHEXT.

    How to use:

    CommandLinePathResolver.TryGetFullPathForCommand("calc.exe"); // C:\WINDOWS\system32\calc.exe
    
    CommandLinePathResolver.TryGetFullPathForCommand("wordpad"); // C:\Program Files\Windows NT\Accessories\WORDPAD.EXE
    

    Here is the code:

    internal static class CommandLinePathResolver
    {
        private const int MAX_PATH = 260;
        private static Lazy> appPaths = new Lazy>(LoadAppPaths);
        private static Lazy executableExtensions = new Lazy(LoadExecutableExtensions);
    
        public static string TryGetFullPathForCommand(string command)
        {
            if (Path.HasExtension(command))
                return TryGetFullPathForFileName(command);
    
            return TryGetFullPathByProbingExtensions(command);
        }
    
        private static string[] LoadExecutableExtensions() => Environment.GetEnvironmentVariable("PATHEXT").Split(';');
    
        private static Dictionary LoadAppPaths()
        {
            var appPaths = new Dictionary(StringComparer.OrdinalIgnoreCase);
    
            using var key = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\App Paths");
            foreach (var subkeyName in key.GetSubKeyNames())
            {
                using var subkey = key.OpenSubKey(subkeyName);
                appPaths.Add(subkeyName, subkey.GetValue(string.Empty)?.ToString());
            }
    
            return appPaths;
        }
    
        private static string TryGetFullPathByProbingExtensions(string command)
        {
            foreach (var extension in executableExtensions.Value)
            {
                var result = TryGetFullPathForFileName(command + extension);
                if (result != null)
                    return result;
            }
    
            return null;
        }
    
        private static string TryGetFullPathForFileName(string fileName) =>
            TryGetFullPathFromPathEnvironmentVariable(fileName) ?? TryGetFullPathFromAppPaths(fileName);
    
        private static string TryGetFullPathFromAppPaths(string fileName) =>
            appPaths.Value.TryGetValue(fileName, out var path) ? path : null;
    
        private static string TryGetFullPathFromPathEnvironmentVariable(string fileName)
        {
            if (fileName.Length >= MAX_PATH)
                throw new ArgumentException($"The executable name '{fileName}' must have less than {MAX_PATH} characters.", nameof(fileName));
    
            var sb = new StringBuilder(fileName, MAX_PATH);
            return PathFindOnPath(sb, null) ? sb.ToString() : null;
        }
    
        [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, SetLastError = false)]
        private static extern bool PathFindOnPath([In, Out] StringBuilder pszFile, [In] string[] ppszOtherDirs);
    }
    

提交回复
热议问题