检查Windows路径中是否存在可执行文件

时间:2010-10-04 14:01:17

标签: c# .net file

如果我使用ShellExecute(或带有System.Diagnostics.Process.Start()的.net)运行进程,则启动的文件名进程不需要是完整路径。

如果我想开始记事本,我可以使用

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中执行此任务?

8 个答案:

答案 0 :(得分:51)

我认为没有任何内置内容,但您可以使用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;
}

答案 1 :(得分:27)

这是有风险的,除了在PATH中搜索目录之外还有很多其他内容。试试这个:

 Process.Start("wordpad.exe");

可执行文件存储在我的计算机上的c:\ Program Files \ Windows NT \ Accessories中,该目录在路径上

HKCR \ Applications和HKLM \ SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ App Paths键也在查找可执行文件中发挥作用。我很确定还有这样的地雷,64位版本的Windows中的目录虚拟化可能会让你失望。

为了使这更可靠,我认为你需要pinvoke AssocQueryString()。不确定,从来没有必要。更好的方法当然是不必问这个问题。

答案 2 :(得分:15)

好的,我认为这是一种更好的方式......

这使用 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");
    }
}

答案 3 :(得分:6)

接受的答案指出没有内置函数,但这不是事实。 有一个标准的WinAPI PathFindOnPath用于执行此操作,自Windows 2000开始可用。

答案 4 :(得分:5)

我尝试了Dunc的“ where”过程,该过程可以运行,但是它很慢且占用大量资源,并且存在孤立过程的轻微危险。

我喜欢尤金·马拉(Eugene Mala)关于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;

答案 5 :(得分:2)

我正在做同样的事情,我认为我现在最好的选择是使用对CreateProcess的本地调用来创建一个暂停的进程并注意成功;之后立即终止该过程。终止暂停的过程不应导致任何资源流失[需要引证:)]

我可能无法弄清楚实际使用的路径,但是对于一个简单的要求,ExistsOnPath()应该这样做 - 直到有更好的解决方案。

答案 6 :(得分:2)

短而直接,这正是发布者想要的。

FILE *fp
char loc_of_notepad[80] = "Not Found";

// Create a pipe to run the build-in where command
// It will return the location of notepad
fp = popen("cmd /C where notepad", "r");
// Read a line from the pipe, if notepad is found 
// this will be the location (followed by a '\n')
fgets(loc_of_notepad, 80, fp);
fclose(fp);

printf("Notepad Location: %s", loc_of_notepad);

答案 7 :(得分:0)

我结合了@Ron和@Hans Passant的答案,创建了一个类,该类通过调用select(option: MatSelectChange, oldValue: string): void { this.openConfirmDeletionDialog().pipe( filter((respose) => { if(Boolean(reponse) === false) { this.selectedTextCountingType = this.selectedTextCountingType.find(countingType => countingType.value === oldValue); } return Boolean(reponse); }) ) } 来检查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