Процесс.Start() и переменная среды PATH

#c#

#c# #путь #process.start

Вопрос:

У меня есть следующее тривиальное приложение на C #, которое просто пытается запустить «jconsole.exe «, который на моей машине находится в C:Programsjdk16bin .

 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:windowssytem32;c:programsjdk16bin
  

это работает отлично. Однако, если переменная среды PATH установлена в

 c:windows;c:windowssytem32;c:\programsjdk16bin
  

(обратите внимание на две обратные косые черты между «c:» и «программы»), сбой с исключением 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, похоже, нет проблем с поиском исполняемого файла с двойной обратной косой чертой в ПУТИ, но процесс.Start() выполняет.

Почему дополнительная обратная косая черта в ПУТИ вызывает проблемы и как я могу обойти проблему? Я не знаю, где исполняемый файл, который я хочу вызвать, будет находиться во время выполнения, поэтому я бы предпочел полагаться на переменную PATH .

Комментарии:

1. Существует два способа запуска EXE-файла, вы тестируете оба способа. Ваше приложение использует ShellExecuteEx(), интерпретатор командной строки использует CreateProcess() . Вы можете поиграть со свойством ProcessStartInfo.UseShellExecute. Нет особого смысла беспокоиться о том, как они по-разному интерпретируют переменную среды 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);
}
  

Я нашел один пункт, который дал мне идею для этого решения. Из документации для процесса.Начать

Если у вас есть переменная path, объявленная в вашей системе с использованием кавычек, вы должны полностью указать этот путь при запуске любого процесса, найденного в этом расположении. В противном случае система не найдет путь. Например, если c:mypath не находится на вашем пути, и вы добавляете его, используя кавычки: path = %path%;»c:mypath «, вы должны полностью квалифицировать любой процесс в c:mypath при его запуске.

То, как я это прочитал, несмотря PATH на то, что переменная содержала допустимый путь, который может использовать Windows, Process.Start не может его использовать и нуждается в полном пути .

Комментарии:

1. Спасибо, что выделили абзац из документации Amith. Я интерпретировал это как влияющее только на записи в пути с кавычками, но мне нравится ваше обобщение, что вы не можете доверять процессу. Start() для правильного использования переменной среды PATH. В качестве интересного, я попытался задать свой ПУТЬ c:windowssystem32;c:windows;"c:programsjdk16bin" и обработать. Start() смог найти jconsole.exe без какой-либо дополнительной помощи. Это, похоже, противоречит тому, что говорится в документах о процессе. Запуск () с использованием ПУТИ, поэтому я действительно не доверяю ему сейчас. 🙂

2. Я рекомендую использовать System.IO.Path.pathSeparator для поддержки нескольких платформ. В macos разделителем путей является «:».

Ответ №2:

Вы можете решить эту проблему, если сначала создадите ProcessStartInfo .

 ProcessStartInfo psi = new ProcessStartInfo("jconsole.exe");
StringDictionary dictionary = psi.EnvironmentVariables;

// Manipulate dictionary...

psi.EnvironmentVariables["PATH"] = dictionary.Replace(@"\", @"");
Process.Start(psi);
  

Вам придется самому выяснить, как управлять этим ПУТЕМ, чтобы он работал на вас. Но это должно решить любые проблемы, которые могут возникнуть у вас с вашей переменной PATH.

Комментарии:

1. После изменения свойства EnvironmentVariables вы должны установить для свойства UseShellExecute значение false . Однако, если значение UseShellExecute равно false , я должен указать полный путь для свойства FileName , что противоречит цели изменения пути.

2. Тем не менее , в самом примере на UseShellExecute странице они не дают полной квалификации FileName и использования UseShellExecute = false; . Я также попытался установить для него значение false и просто вызвать exe-файл, который можно найти только в моем PATH, и он просто запускается.

3. я бы также рекомендовал установить ваш Process.StartInfo.WorkingDirectory путь к среде, который ваш .exe должен использовать

Ответ №3:

Принятый ответ неверен.

cmd.exe сначала найдет приложения с исполняемыми расширениями.
Итак, когда у вас есть файлы puma и puma.bat в C:Rubybin , тогда puma.bat они будут иметь приоритет над puma .

Если вы начнете c:rubybinpuma.bat с c:redmine , он запустит puma с текущего рабочего каталога c:rubybin , и ваше веб-приложение будет работать.
Однако, если вы запустите c:rubybinpuma напрямую, он запустит 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}:rn{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}:rn{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