我结合了@Ron和@Hans Passant的答案,创建了一个类来检查文件路径,它会在App Path
注册表键和PATH
中调用PathFindOnPath
。它还允许省略文件扩展名。在这种情况下,它会从PATHEXT
中探测几个可能的“可执行”文件扩展名。
如何使用:
CommandLinePathResolver.TryGetFullPathForCommand("calc.exe"); // C:\WINDOWS\system32\calc.exe
CommandLinePathResolver.TryGetFullPathForCommand("wordpad"); // C:\Program Files\Windows NT\Accessories\WORDPAD.EXE
这里是代码:
internal static class CommandLinePathResolver
{
private const int MAX_PATH = 260;
private static Lazy<Dictionary<string, string>> appPaths = new Lazy<Dictionary<string, string>>(LoadAppPaths);
private static Lazy<string[]> executableExtensions = new Lazy<string[]>(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<string, string> LoadAppPaths()
{
var appPaths = new Dictionary<string, string>(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);
}
PATH
是特定于平台的(例如,子目录的/
与\
,以及条目分隔符的:
与;
),并且希望一致地处理模糊结果 - 因此它不像“获取路径然后检查exists()”那样简单。此外,如果有数十个要检查的路径,或者任何一个是远程/网络路径,则在程序代码中进行检查可能会对性能产生重大影响,例如,而不是操作系统具有预缓存结果。等等。 - Dai