Проверьте, существует ли исполняемый файл в пути Windows - PullRequest
56 голосов
/ 04 октября 2010

Если я запускаю процесс с ShellExecute (или в .net с System.Diagnostics.Process.Start()), для запуска процесса имени файла не обязательно указывать полный путь.

Если я хочу запустить блокнот,Я могу использовать

Process.Start("notepad.exe");

вместо

Process.Start(@"c:\windows\system32\notepad.exe");

, потому что direcotry 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?

Ответы [ 6 ]

53 голосов
/ 04 октября 2010

Я думаю, что ничего встроенного нет, но вы могли бы сделать что-то подобное с 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;
}
27 голосов
/ 04 октября 2010

Это рискованно, это намного больше, чем просто поиск по каталогам в PATH. Попробуйте это:

 Process.Start("wordpad.exe");

Исполняемый файл хранится в папке c: \ Program Files \ Windows NT \ Accessories на моем компьютере, этот каталог , а не в пути.

Ключи HKCR \ Applications и HKLM \ SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ App Paths также играют роль в поиске исполняемых файлов. Я вполне уверен, что есть и другие подобные мины, например, виртуализация каталогов в 64-битных версиях Windows может сбить вас с толку, например.

Чтобы сделать это более надежным, я думаю, что вам нужно вызвать функцию AssocQueryString (). Не уверен, никогда не было необходимости. Лучше всего, конечно, не задавать вопрос.

15 голосов
/ 25 июня 2014

Хорошо, я думаю, лучше ...

Используется команда , где , которая доступна по крайней мере в 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");
    }
}
6 голосов
/ 20 июня 2018

Принятый ответ гласит, что ничего встроенного нет, но это не так.Для этого существует стандартный WinAPI PathFindOnPath , он доступен начиная с Windows 2000.

5 голосов
/ 21 сентября 2018

Я опробовал у Дунка процесс "где", и он работает, но он медленный и ресурсоемкий, и есть небольшая опасность иметь потерянный процесс.

Мне нравится совет Юджина Мала о 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;
2 голосов
/ 08 декабря 2010

Я стремлюсь к тому же, и я думаю, что лучший вариант, который у меня есть сейчас, - это использовать собственный вызов CreateProcess для создания приостановленного процесса и отслеживания успеха; прекращение процесса сразу после этого. Прекращение приостановленного процесса не должно приводить к кровотечению ресурса [цитата нужна:)]

Возможно, я не смогу выяснить путь, который фактически использовался, но для простого требования, как ExistsOnPath (), он должен это делать - пока не найдется лучшее решение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...