Может ли Process.Start() учитывать системный PATH?

Я искал и экспериментировал некоторое время с этим, но мне не повезло.

Я пытаюсь создать консольную программу для автоматизации некоторых задач, которые я не мог выполнить с файлом BAT. Я хочу вызвать «signcode.exe» из Windows SDK, папки bin со всеми инструментами в моей системной PATH, и я могу вызвать «signcode» из любого места, но Process.Start игнорирует путь.

Текущий код:

System.Diagnostics.Process sign = new System.Diagnostics.Process();
sign.StartInfo.FileName         = signCommand.Substring(0, signCommand.IndexOf(' '));  // signtool.exe
sign.StartInfo.Arguments        = signCommand.Substring(signCommand.IndexOf(' ') + 1); // /sign /a file1 file2

// sign.StartInfo.EnvironmentVariables["Path"] = Environment.GetEnvironmentVariable("PATH");  // This doesn't work either
sign.StartInfo.UseShellExecute              = false;
sign.StartInfo.RedirectStandardOutput       = true;
sign.StartInfo.RedirectStandardError        = true;

sign.Start();  // Throws Win32Exception - The system cannot find the file specified

Я подтвердил, что StartInfo.EnvironmentVariables["Path"] соответствует моему системному пути и содержит папку Windows SDK. Установка вручную тоже не работает.

Я даже пытался установить TempPath, как показано на странице MSDN для EnvironmentVariables Property", но это тоже не сработало. Интересно, почему вы могли бы установить это, если это не имеет никакого эффекта.

Если System.Diagnostics.Process не может использовать путь, есть ли другие функции, которые я мог бы использовать? Я также хотел бы увидеть вывод команды в моем консольном приложении.

Вот некоторые дополнительные значения отладки:

Console.WriteLine("Sign Filename = '{0}'", sign.StartInfo.FileName);
Sign Filename = 'signtool.exe'

Console.WriteLine("Sign Arguments = '{0}'", sign.StartInfo.Arguments);
Sign Arguments = '/sign /f C:\Visual Studio\Projects\MGInsight\MGInsight\APPARENTINC.pfx /t http://timestamp.comodoca.com/authenticode "C:\Visual Studio\Projects\MGInsight\MGInsight\Publish\Application Files\\MGInsight_0_9_1_85\MGInsight.exe" "C:\Visual Studio\Projects\MGInsight\MGInsight\Publish\Application Files\\MGInsight_0_9_1_85\XPXScanner.dll" "C:\Visual Studio\Projects\MGInsight\MGInsight\Publish\Application Files\\MGInsight_0_9_1_85\NetworkCalculations.dll"'

Console.WriteLine("Sign Path = '{0}'", sign.StartInfo.EnvironmentVariables["Path"]);
Sign Path = 'C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;"C:\Program Files\Intel\WiFi\bin\";"C:\Program Files\Common Files\Intel\WirelessCommon\";"C:\Program Files (x86)\cwRsync\bin";"C:\Program Files (x86)\Git\cmd";"C:\Program Files (x86)\Git\bin";"C:\Program Files (x86)\Zend\ZendServer\bin";"C:\Program Files (x86)\Zend\ZendServer\share\ZendFramework\bin";"C:\Program Files\Java\jre6\bin";"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\";"C:\Program Files\Microsoft Windows Performance Toolkit\";C:\MinGW\bin;"C:\Program Files (x86)\Microsoft\ILMerge";"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin";C:\Program Files (x86)\Nmap'

Путь "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin" находится там, где находится signtool.exe, и я могу запустить его из командной строки, просто набрав signtool, но если я запущу это приложение из того же приглашения, оно не зарегистрирует этот путь.


person drew010    schedule 03.11.2011    source источник
comment
Не работает - нет ошибки. Какое исключение вы получаете? Что касается выходной части вашего вопроса, посмотрите StandardOutput .   -  person CodeCaster    schedule 04.11.2011
comment
Исключение и сообщение, которое оно выдает, комментируются после sign.Start(), это Win32Exception.   -  person drew010    schedule 04.11.2011
comment
Вы правы, я не читал блок кода. Разве Win32Exception не исходит от самого инструмента подписи? Что он не может найти файл, который вы хотите подписать? Попробуйте запустить его с параметром /h или другим параметром, для которого не требуется файл.   -  person CodeCaster    schedule 04.11.2011
comment
@CodeCaster Не беспокойтесь, я должен был уточнить вопрос, я попробую и посмотрю, что у меня получится, спасибо!   -  person drew010    schedule 04.11.2011
comment
Я пробовал знак signtool.exe /? но все же получил исключение вместо вывода содержимого справки в STDOUT.   -  person drew010    schedule 04.11.2011
comment
Путь не должен содержать кавычек.   -  person Christopher G. Lewis    schedule 13.10.2014
comment
Будьте осторожны при добавлении пути в Windows 10 — новый виджет списка автоматически добавит кавычки вокруг вашего пути — если вы используете кнопку «Редактировать текст...», вы можете удалить кавычки.   -  person Stefan Drissen    schedule 17.03.2016


Ответы (7)


Я почти уверен, что Process.Start уважает PATH.

  • Вы уверены, что ваше значение signCommand правильное?
  • Указано ли значение каталога в PATH с использованием кавычек? В документах упоминается, что такие значения не будут соблюдаться.

Обратите внимание, что FileName также может быть полным путем к исполняемому файлу.

person Mikayla Hutchinson    schedule 03.11.2011
comment
Я хочу избежать использования полного пути к исполняемому файлу, поскольку он может находиться в разных местах. Мой путь цитирует его, потому что он находится в C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin. Я думаю, что это должно быть заключено в кавычки, потому что в пути есть пробелы, я также заметил из документов, что FileNotFoundException выдается, если PATH содержит кавычки, но я получаю Win32Exception - person drew010; 04.11.2011
comment
Я не вижу упоминания о FileNotFoundException... Win32Exception - это то, что он всегда выдает, если не может найти исполняемый файл. Что странно, так это то, что документы для Process.Start упоминают об использовании значений PATH, но документы Win32 CreateProcess явно говорят, что путь поиска не используется, поэтому я предполагаю, что Process.Start истинно для разрешения полного имени с использованием PATH перед передачей значение для CreateProcess, и, вероятно, оно ограничено тем, как он анализирует/разбивает PATH. - person Mikayla Hutchinson; 04.11.2011
comment
Вы всегда можете искать PATH вручную: string ResolveFullPath (строковое имя) { return fullName = Path.IsPathRooted(name)? имя: Environment.GetEnvironmentVariable(PATH).Split(Path.PathSeparator).Select(dir => Path.Combine(dir.Trim(''), name)).First(File.Exists); } - person Mikayla Hutchinson; 04.11.2011
comment
Меня поймали здесь, используя maven из командной строки в Windows - он был в пути, но просто использование «mvn» не сработало, так как это был пакетный файл — вместо этого вам нужно использовать «mvn.bat» - person JonnyRaa; 28.01.2014

Добавление к ответу mhutch: он действительно учитывает PATH, но я заметил, что вам действительно нужно перезапустить Visual Studio, чтобы принять любые изменения пути. Это как-то подло.

person Chompski    schedule 04.10.2012
comment
Вау, вы только что сэкономили мне 1 час (или больше) на поиске этого... :) - person Guillermo; 31.07.2013
comment
Использовал пару часов, чтобы попытаться понять это, пока не наткнулся на это решение. Ух ты :) - person Lasse Vabe Rolstad; 22.02.2017

Если вы недавно обновили PATH, обязательно перезапустите Visual Studio. Переменные среды загружаются при запуске Visual Studio. Обратите внимание, что это относится к выполнению в режиме DEBUG.

person nuzzolilo    schedule 07.11.2012

Ну, я думаю, проблема была связана с тем, что сказал mhutch из документации MSDN:

If you have a path variable declared in your system using quotes, 
you must fully qualify that path when starting any process found 
in that location. Otherwise, the system will not find the path. For
example, if c:\mypath is not in your path, and you add it using
quotation marks: path = %path%;"c:\mypath", you must fully qualify
any process in c:\mypath when starting it.

Сначала я это видел, но это показалось странным, поэтому я не придал этому значения. Не уверен, почему это так, но, похоже, это так.

Я попытался скопировать signtool.exe в C:\sign\tool\bin и добавил это в свой путь, и тогда мой код заработал, так что я думаю, потому что у меня есть кавычки в этом пути из-за пробелов, я SOL и мне придется вручную найдите путь к пути Windows SDK, если нет способа добавить что-то с пробелами к пути без использования кавычек.

person drew010    schedule 04.11.2011


Имя файла StartInfo на самом деле является полным путем к исполняемому файлу.

Например, на обертке у меня для x264 это выглядит так:

x264Start.FileName = Directory.GetCurrentDirectory() + @"\Tools\x264\x264x86-r1995.exe";

Я бы проверил код, добавив туда try {}, catch {} и написав фактическое имя файла, который вы пытаетесь вызвать в отладке, если у вас есть ошибка.

В противном случае добавьте что-то вроде следующего:

    if (File.exists(sign.FileName))
    {
        sign.Start();
    }
    else
    {
        Console.WriteLine("Can't find {0}", sign.FileName);
        throw new Exception("File doesn't exist");
    }

РЕДАКТИРОВАТЬ: добавлен полный пример — он ищет CCleaner, а затем запускает его с помощью переключателя «/ AUTO». Только что проверил и работает нормально.

        // detect if 64-bit system
        string programFiles = "";
        if (Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE").Contains("64"))
        {
            Console.WriteLine("#info# x64 detected");
            programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
        }
        else
        {
            Console.WriteLine("#info# x86 detected");
            programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
        }

        // search
        string[] dirs = Directory.GetDirectories(programFiles, "CCleaner", SearchOption.AllDirectories);
        string[] exes = Directory.GetFiles(programFiles, "CCleaner64.exe", SearchOption.AllDirectories);

        //debug only
        foreach (string s in dirs)
        {
            Console.WriteLine(s);
        }

        foreach (string s in exes)
        {
            Console.WriteLine(s);
        }

        // access directly
        ProcessStartInfo CCleaner = new ProcessStartInfo(exes[0], "/AUTO");
        Process.Start(CCleaner);
person James    schedule 03.11.2011

Ваш код, кажется, учитывает путь для меня.

Трудно сказать, что может быть не так в вашем случае, но вы можете попробовать запустить свою команду через cmd.exe:

sign.StartInfo.FileName = "cmd";
sign.StartInfo.Arguments = "/c signtool.exe ...";
person ladenedge    schedule 03.11.2011