private static string ResolvePath(string filename)
{
// Follow the same resolution that Windows uses with CreateProcess:
// 1. First try the exact path provided
// 2. Then try the file relative to the executable directory
// 3. Then try the file relative to the current directory
// 4. then try the file in each of the directories specified in PATH
// Windows does additional Windows-specific steps between 3 and 4,
// and we ignore those here.
// If the filename is a complete path, use it, regardless of whether it exists.
if (Path.IsPathRooted(filename))
{
// In this case, it doesn't matter whether the file exists or not;
// it's what the caller asked for, so it's what they'll get
return filename;
}
// Then check the executable's directory
string path = GetExePath();
if (path != null)
{
try
{
path = Path.Combine(Path.GetDirectoryName(path), filename);
if (File.Exists(path))
{
return path;
}
}
catch (ArgumentException) { } // ignore any errors in data that may come from the exe path
}
// Then check the current directory
path = Path.Combine(Directory.GetCurrentDirectory(), filename);
if (File.Exists(path))
{
return path;
}
// Then check each directory listed in the PATH environment variables
string pathEnvVar = Environment.GetEnvironmentVariable("PATH");
if (pathEnvVar != null)
{
var pathParser = new StringParser(pathEnvVar, ':', skipEmpty: true);
while (pathParser.MoveNext())
{
string subPath = pathParser.ExtractCurrent();
path = Path.Combine(subPath, filename);
if (File.Exists(path))
{
return path;
}
}
}
// Could not find the file
throw new Win32Exception(Interop.Error.ENOENT.Info().RawErrno);
}