Проверьте, существует ли исполняемый файл в пути Windows
Если я запускаю процесс с помощью ShellExecute
(или в .net с System.Diagnostics.Process.Start()
), процесс имени файла для запуска не обязательно должен быть полным путем.
Если я хочу запустить блокнот, я могу использовать
Process.Start("notepad.exe");
вместо
Process.Start(@"c:\windows\system32\notepad.exe");
потому что директиву c:\windows\system32
является частью переменной среды PATH.
как я могу проверить, существует ли файл в PATH без выполнения процесса и без разбора переменной PATH?
System.IO.File.Exists("notepad.exe"); // returns false
(new System.IO.FileInfo("notepad.exe")).Exists; // returns false
но мне нужно что-то вроде этого:
System.IO.File.ExistsOnPath("notepad.exe"); // should return true
и
System.IO.File.GetFullPath("notepad.exe"); // (like unix which cmd) should return
// c:\windows\system32\notepad.exe
Есть ли предопределенный класс для выполнения этой задачи в BCL?
Ответы
Ответ 1
Я думаю, что ничего встроенного нет, но вы можете сделать что-то подобное с System.IO.File.Exists:
public static bool ExistsOnPath(string fileName)
{
return GetFullPath(fileName) != null;
}
public static string GetFullPath(string fileName)
{
if (File.Exists(fileName))
return Path.GetFullPath(fileName);
var values = Environment.GetEnvironmentVariable("PATH");
foreach (var path in values.Split(Path.PathSeparator))
{
var fullPath = Path.Combine(path, fileName);
if (File.Exists(fullPath))
return fullPath;
}
return null;
}
Ответ 2
Это рискованно, там гораздо больше, чем просто поиск каталогов в PATH. Попробуйте следующее:
Process.Start("wordpad.exe");
Исполняемый файл хранится в каталоге c:\Program Files\Windows NT\Accessories на моей машине, этот каталог не находится на пути.
Ключи HKCR\Applications и HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths также играют роль в поиске исполняемых файлов. Я вполне уверен, что в этом есть дополнительные наземные мины, виртуализация каталогов в 64-разрядных версиях Windows может, например, вас заманить.
Чтобы сделать это более надежным, я думаю, вам нужно pinvoke AssocQueryString(). Не уверен, никогда не было необходимости. Лучший подход, безусловно, не должен задавать вопрос.
Ответ 3
Хорошо, я думаю, лучше...
Здесь используется команда where, которая доступна по крайней мере в Windows 7/Server 2003:
public static bool ExistsOnPath(string exeName)
{
try
{
using (Process p = new Process())
{
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = "where";
p.StartInfo.Arguments = exeName;
p.Start();
p.WaitForExit();
return p.ExitCode == 0;
}
}
catch(Win32Exception)
{
throw new Exception("'where' command is not on path");
}
}
public static string GetFullPath(string exeName)
{
try
{
using (Process p = new Process())
{
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = "where";
p.StartInfo.Arguments = exeName;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
if (p.ExitCode != 0)
return null;
// just return first match
return output.Substring(0, output.IndexOf(Environment.NewLine));
}
}
catch(Win32Exception)
{
throw new Exception("'where' command is not on path");
}
}
Ответ 4
Принятый ответ гласит, что ничего встроенного нет, но это не так. Для этого существует стандартный WinAPI PathFindOnPath, он доступен начиная с Windows 2000.
Ответ 5
Я опробовал Dunc процесс "where", и он работает, но он медленный и ресурсоемкий, и там есть небольшая опасность иметь потерянный процесс.
Мне нравится совет Юджина Мала о PathFindOnPath, поэтому я уточнил это как полный ответ. Это то, что я использую для нашего собственного инструмента.
/// <summary>
/// Gets the full path of the given executable filename as if the user had entered this
/// executable in a shell. So, for example, the Windows PATH environment variable will
/// be examined. If the filename can't be found by Windows, null is returned.</summary>
/// <param name="exeName"></param>
/// <returns>The full path if successful, or null otherwise.</returns>
public static string GetFullPathFromWindows(string exeName)
{
if (exeName.Length >= MAX_PATH)
throw new ArgumentException($"The executable name '{exeName}' must have less than {MAX_PATH} characters.",
nameof(exeName));
StringBuilder sb = new StringBuilder(exeName, MAX_PATH);
return PathFindOnPath(sb, null) ? sb.ToString() : null;
}
// https://docs.microsoft.com/en-us/windows/desktop/api/shlwapi/nf-shlwapi-pathfindonpathw
// https://www.pinvoke.net/default.aspx/shlwapi.PathFindOnPath
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode, SetLastError = false)]
static extern bool PathFindOnPath([In, Out] StringBuilder pszFile, [In] string[] ppszOtherDirs);
// from MAPIWIN.h :
private const int MAX_PATH = 260;
Ответ 6
Я за то же самое, и я думаю, что лучшим вариантом, который у меня есть сейчас, является использование собственного вызова CreateProcess для создания приостановленного процесса и наблюдения за успехом; завершая процесс сразу же после этого. Прекращение приостановленного процесса не должно вызывать кровотечения ресурса [citation needed:]]
Возможно, мне не удастся выяснить путь, который действительно использовался, но для простого требования, как ExistsOnPath(), которое он должен делать, до тех пор, пока не будет лучшего решения.