Переменные среды Process.Start() и PATH
У меня есть следующее тривиальное приложение С#, которое просто пытается запустить "jconsole.exe", который на моем компьютере находится в C:\Programs\jdk16\bin.
using System;
using System.Diagnostics;
namespace dnet {
public class dnet {
static void Main( string[] args ) {
try {
Process.Start("jconsole.exe");
Console.WriteLine("Success!");
} catch (Exception e) {
Console.WriteLine("{0} Exception caught.", e);
}
}
}
}
Если моя переменная среды PATH установлена на
c:\windows;c:\windows\sytem32;c:\programs\jdk16\bin
работает отлично. Однако, если для переменной среды PATH установлено значение
c:\windows;c:\windows\sytem32;c:\\programs\jdk16\bin
(обратите внимание на две обратные косые черты между "c:" и "program" ), сбой с исключением win32.
System.ComponentModel.Win32Exception (0x80004005): The system cannot find the file specified
at System.Diagnostics.Process.StartWithShellExecuteEx(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start(ProcessStartInfo startInfo)
at dnet.dnet.Main(String[] args)
Интересно, что в той же командной строке, где я запускаю .NET-программу и получаю исключение, могу просто набрать "jconsole.exe", и программа запустится. У Windows, похоже, нет проблем с поиском исполняемого файла с двойной обратной косой чертой в PATH, но Process.Start() делает.
Почему дополнительная обратная косая черта в PATH вызывает проблемы и как я могу обойти эту проблему? Я не знаю, где исполняемый файл, который я хочу вызвать, будет находиться во время выполнения, поэтому я предпочел бы полагаться на переменную PATH.
Ответы
Ответ 1
Не совсем уверен, почему возникает проблема. Хотя, я могу думать о одном решении, которое работает на моей машине:
var enviromentPath = System.Environment.GetEnvironmentVariable("PATH");
Console.WriteLine(enviromentPath);
var paths = enviromentPath.Split(';');
var exePath = paths.Select(x => Path.Combine(x, "mongo.exe"))
.Where(x => File.Exists(x))
.FirstOrDefault();
Console.WriteLine(exePath);
if (string.IsNullOrWhiteSpace(exePath) == false)
{
Process.Start(exePath);
}
Я нашел один пара, который дал мне идею для этого решения. Из документации для Process.Start
Если у вас есть переменная пути, объявленная в вашей системе, используя кавычки, вы должен полностью квалифицировать этот путь при запуске любого процесса, найденного в этом место нахождения. В противном случае система не найдет путь. Например, если c:\mypath не находится на вашем пути, и вы добавляете его, используя котировку mark: path =% path%; "c:\mypath", вы должны полностью квалифицировать любой процесс в c:\mypath при запуске.
Как я его читал, хотя переменная PATH
содержала допустимый путь, который может использовать Windows, Process.Start
не может использовать его и нуждается в полном пути .
Ответ 2
Вы можете решить эту проблему, если сначала создаете ProcessStartInfo
.
ProcessStartInfo psi = new ProcessStartInfo("jconsole.exe");
StringDictionary dictionary = psi.EnvironmentVariables;
// Manipulate dictionary...
psi.EnvironmentVariables["PATH"] = dictionary.Replace(@"\\", @"\");
Process.Start(psi);
Вам нужно будет выяснить, как управлять PATH, чтобы он работал для вас. Но это должно решить любые проблемы, которые могут возникнуть с вашей переменной PATH.
Ответ 3
Принятый ответ неверен.
cmd.exe сначала найдет приложения с исполняемыми расширениями.
Поэтому, когда у вас есть файлы puma
и puma.bat
в C:\Ruby\bin\
, тогда buma.bat
будет иметь приоритет над puma.
Если вы запустите c:\ruby\bin\puma.bat
из c:\redmine
, он запустит puma с текущим рабочим каталогом c:\ruby\bin
, и ваше веб-приложение будет работать.
Если вы запустите c:\ruby\bin\puma
напрямую, он запустит puma с текущим рабочим каталогом в c:\redmine
и впоследствии потерпит неудачу.
Итак, исправленная версия выглядит примерно так:
// FindAppInPathDirectories("ruby.exe");
public string FindAppInPathDirectories(string app)
{
string enviromentPath = System.Environment.GetEnvironmentVariable("PATH");
string[] paths = enviromentPath.Split(';');
foreach (string thisPath in paths)
{
string thisFile = System.IO.Path.Combine(thisPath, app);
string[] executableExtensions = new string[] { ".exe", ".com", ".bat", ".sh", ".vbs", ".vbscript", ".vbe", ".js", ".rb", ".cmd", ".cpl", ".ws", ".wsf", ".msc", ".gadget" };
foreach (string extension in executableExtensions)
{
string fullFile = thisFile + extension;
try
{
if (System.IO.File.Exists(fullFile))
return fullFile;
}
catch (System.Exception ex)
{
Log("{0}:\r\n{1}",
System.DateTime.Now.ToString(m_Configuration.DateTimeFormat, System.Globalization.CultureInfo.InvariantCulture)
, "Error trying to check existence of file \"" + fullFile + "\""
);
Log("Exception details:");
Log(" - Exception type: {0}", ex.GetType().FullName);
Log(" - Exception Message:");
Log(ex.Message);
Log(" - Exception Stacktrace:");
Log(ex.StackTrace);
} // End Catch
} // Next extension
} // Next thisPath
foreach (string thisPath in paths)
{
string thisFile = System.IO.Path.Combine(thisPath, app);
try
{
if (System.IO.File.Exists(thisFile))
return thisFile;
}
catch (System.Exception ex)
{
Log("{0}:\r\n{1}",
System.DateTime.Now.ToString(m_Configuration.DateTimeFormat, System.Globalization.CultureInfo.InvariantCulture)
, "Error trying to check existence of file \"" + thisFile + "\""
);
Log("Exception details:");
Log(" - Exception type: {0}", ex.GetType().FullName);
Log(" - Exception Message:");
Log(ex.Message);
Log(" - Exception Stacktrace:");
Log(ex.StackTrace);
} // End Catch
} // Next thisPath
return app;
} // End Function FindAppInPathDirectories