Can Process.Start() take the system PATH into account?

13,136

Solution 1

I'm pretty sure Process.Start does respect PATH.

  • Are you sure your signCommand value is correct?
  • Is the directory value in PATH specified using quotes? The docs mention that such values will not be respected.

Note that FileName can also be a full path to the executable.

Solution 2

Adding to mhutch's answer: It does indeed take PATH into consideration, but I have noticed you actually need to restart Visual Studio to pick up any path changes. It is kind of sneaky.

Solution 3

If you updated PATH recently, be sure to restart Visual Studio. Environment variables are loaded at the launch of Visual Studio. Note that this applies to DEBUG mode execution.

Solution 4

Well, I guess the problem was related to what mhutch said, from the MSDN docs:

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.

I saw that initially, but it seemed strange so I disregard it. Not sure why that's the case but it seems to be.

I tried copying signtool.exe to C:\sign\tool\bin and added that to my path, and then my code worked, so I guess because I have quotes in that path due to the spaces, I am SOL and will have to manually search the path for the windows SDK path unless there is some way to add something with spaces to the path without using quotes.

Solution 5

The StartInfo filename is actually the full path to the executable

For example, on a wrapper I have for x264, it looks like this:

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

I'd sense-check the code by adding a try {}, catch {} in there and writing the actual filename you are trying to call into the debug if you have an error.

Otherwise add something like the below:

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

EDIT: Added a full example - this searches for CCleaner and then runs it with the "/AUTO" switch. Just tested and works fine.

        // 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);
Share:
13,136
drew010
Author by

drew010

https:// drew-phillips.com / contact

Updated on June 17, 2022

Comments

  • drew010
    drew010 almost 2 years

    I've been searching and experimenting for a while with this, but I have had no luck.

    I am trying to make a console program to automate some tasks that I couldn't quite do with a BAT file. I want to call "signcode.exe" from the Windows SDK, the bin folder with all the tools in my system PATH, and I can call "signcode" from anywhere, but Process.Start is ignoring the path.

    Current code:

    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
    

    I've confirmed that StartInfo.EnvironmentVariables["Path"] matches my system path, and contains the Windows SDK folder. Setting it manually doesn't work either.

    I've even tried setting TempPath as shown on the MSDN page for EnvironmentVariables Property, but that didn't work either. I wonder why you would be able to set this if it has no effect.

    If System.Diagnostics.Process cannot use the path, are there any other functions I could use? I'd like to see the output of the command in my console application as well.

    Here is some additional debug values:

    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'
    

    The path "C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin" is where signtool.exe is, and I can run it from a command prompt by simply typing signtool, but if I run this application from the same prompt, it doesn't register that path.

  • drew010
    drew010 over 12 years
    I want to avoid using the full path to the executable since it may be located in different places. My path does quote it because it is in C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin. I think it must be quoted because there are spaces in the path, I also noticed from the docs, that a FileNotFoundException is thrown if the PATH contains quotes, but I am getting a Win32Exception
  • drew010
    drew010 over 12 years
    I don't want to change the working directory since I will run this application from a certain folder and reference files relative to this path and would like that to be the working directory, but I'd prefer not to have to specify the full path to signtool.exe.
  • Mikayla Hutchinson
    Mikayla Hutchinson over 12 years
    I don't see any mention of FileNotFoundException... Win32Exception is what it always throws if it can't find the executable. What's weird is that the docs for Process.Start do mention using PATH values, but the Win32 CreateProcess docs explicitly says that the search path is not used, so I'd guess that Process.Start trues to resolve the full name using PATH before passing the value to CreateProcess, and it's probably limited in how it parses/splits PATH.
  • Mikayla Hutchinson
    Mikayla Hutchinson over 12 years
    You could always search PATH manually: string ResolveFullPath (string name) { return fullName = Path.IsPathRooted(name)? name : Environment.GetEnvironmentVariable("PATH").Split(Path.PathSe‌​parator).Select(dir => Path.Combine(dir.Trim('"'), name)).First(File.Exists); }
  • Guillermo
    Guillermo over 10 years
    Wow, you just saved me 1 hour (or more) looking for this... :)
  • JonnyRaa
    JonnyRaa about 10 years
    I got caught out here using maven from the command line on windows - it was in the path but just using 'mvn' didnt work as it was a batch file - instead you need to use 'mvn.bat'
  • Lasse Vabe Rolstad
    Lasse Vabe Rolstad about 7 years
    Used a couple of hours to try to figure this out, until i stumbled upon this solution. Wow :)