Check if an executable exists in the Windows path
If I run a process with ShellExecute
(or in .net with System.Diagnostics.Process.Start()
) the filename process to start doesn't need to be a full path.
If I want to start notepad, I can use
Process.Start("notepad.exe");
instead of
Process.Start(@"c:\windows\system32\notepad.exe");
because the direcotry c:\windows\system32
is part of the PATH environment variable.
how can I check if a file exists on the PATH without executing the process and without parsing the PATH variable?
System.IO.File.Exists("notepad.exe"); // returns false
(new System.IO.FileInfo("notepad.exe")).Exists; // returns false
but I need something like this:
System.IO.File.ExistsOnPath("notepad.exe"); // should return true
and
System.IO.File.GetFullPath("notepad.exe"); // (like unix which cmd) should return
// c:\windows\system32\notepad.exe
Is there a predefined class to do this task available in the BCL?
I think there's nothing built-in, but you could do something like this with 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;
}
This is risky, there's a lot more to it than just searching the directories in the PATH. Try this:
Process.Start("wordpad.exe");
The executable is stored in c:\Program Files\Windows NT\Accessories on my machine, that directory is not on the path.
The HKCR\Applications and HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths keys also play a role in finding executables. I'm fairly sure there are additional land-mines like this around, directory virtualization in 64-bit versions of Windows could trip you up for example.
To make this more reliable I think you need to pinvoke AssocQueryString(). Not sure, never had the need. The better approach is certainly to not have to ask the question.
Ok, a better way I think...
This uses the where command, which is available at least on 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");
}
}