Проверьте, существует ли исполняемый файл в пути 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?

 Jürgen Steinblock04 окт. 2010 г., 18:10
Да, должно быть очень легко. Но я убежден, что, если задача может быть выполнена с существующей библиотекой языка пробраммирования, я предпочитаю этот способ, а не заново изобретать weel снова и снова. Если нет чего-то доступного, я делаю это самостоятельно.
 mickeyf04 окт. 2010 г., 16:10
Хотя такой предопределенный класс будет удобен (или удобен, если он существует), разве это не просто еще одна строка для получения пути, тогда существует проверка ()? Вы могли бы написать это быстрее, чем задать вопрос. Особая причина / необходимость? Просто интересуюсь.

Ответы на вопрос(6)

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

 Process.Start("wordpad.exe");

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

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

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

 Hans Passant04 окт. 2010 г., 19:11
этоочень Редко в наши дни для установщиков изменить путь. Особенно для утилиты, проверьте это сначала. Я бы просто использовал параметр с областью применения и значением по умолчанию "" здесь.
 Jürgen Steinblock04 окт. 2010 г., 19:07
приложение, которое я хочу запросить, регистрирует себя по пути (mysqldump.exe). Если нет, или если не установлен, я хочу отключить опцию использовать mysqlbackup из приложения Windows Forms. Я просто не хочу жестко кодировать путь к файлу.
 Hans Passant01 авг. 2011 г., 02:52
Это было предметом недавнего поста Рэймонда Чена. Трудно превзойти его навыки ведения блога, кроме меня первым. Наслаждаться:blogs.msdn.com/b/oldnewthing/archive/2011/07/25/10189298.aspx

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

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

 Eugene Mala07 февр. 2019 г., 06:38
Могут быть некоторые части кода, которые будут выполняться, даже если вы создадите приостановленный процесс. Если вы хотите проверить, существует ли какое-либо вредоносное ПО или вирус в системном пути - этот метод очень опасен!

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

Мне нравится совет Юджина Мала о 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;

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

что ничего встроенного нет, но это не так. Есть стандартный WinAPIPathFindOnPath для этого он доступен начиная с Windows 2000.

 Jürgen Steinblock21 июн. 2018 г., 13:44
Спасибо за обновление. Буду иметь в виду.
Решение Вопроса

что нет ничего встроенного, но вы могли бы сделать что-то подобное с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(';'))
    {
        var fullPath = Path.Combine(path, fileName);
        if (File.Exists(fullPath))
            return fullPath;
    }
    return null;
}
 Aaron McIver04 окт. 2010 г., 17:04
Да, это было бы странно при использовании строки. Тем не менее, я думаю, что имеет смысл объединить вышеуказанную функциональность обоих методов в один метод расширения под названием ExistsOnPath, который зависает от FileInfo, как вы упомянули.
 Aaron McIver04 окт. 2010 г., 16:42
Если вы собираетесь это сделать, я предлагаю превратить их в методы расширения ...msdn.microsoft.com/en-us/library/bb383977.aspx
 digEmAll03 июл. 2016 г., 13:40
@GrzegorzAdamHankiewicz: в вопросе конкретно указывается «путь к Windows», так что да, он не переносимый ... в любом случае, спасибо за указание, что с небольшим изменением его можно использовать в Unix-подобных ОС :)
 Aaron McIver04 окт. 2010 г., 18:02
@digEmAll ... да, здесь нет неправильного ответа ...
 digEmAll04 окт. 2010 г., 16:57
@ Аарон: Ты уверен, что увидишьGetFullPath как метод расширения дляstring ? Это звучит странно для меня ... Может быть, может иметь смысл дляFileInfo...
 digEmAll20 июн. 2018 г., 18:36
@EugeneMala: да, но вам нужен p / invoke для вызова этого API (например,pinvoke.net/default.aspx/shlwapi.PathFindOnPath)
 Grzegorz Adam Hankiewicz03 июл. 2016 г., 10:50
Этот код не переносим на другие платформы, на Unix вам нужно использоватьPath.PathSeparator вместо жесткого кодирования точки с запятой.
 digEmAll04 окт. 2010 г., 17:28
@ Аарон: По нескольким причинам (например, почему я должен проходить через FileInfo, если мне нужна только строка ...), я все еще предпочитаю их как статические методы, возможно, обернутые в статический класс Utilities, но я понимаю, что это может быть спорным .. В любом случае, для спрашивающего легко трансформировать приведенный выше код в метод Extension;)
 Eugene Mala20 июн. 2018 г., 16:41
Есть стандартный API:stackoverflow.com/a/50950861/877099

Ваш ответ на вопрос