Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get Environment Variables of Child Process in VC++

Okay, here's essentially what I am trying to do. I have a process P1. This process needs to invoke the Visual Studio command-line compiler cl.exe in a separate process P2 (obviously). However, as everyone who has ever used the Visual Studio command-line compiler knows, you cannot simply invoke cl.exe and expect a good experience. You instead have to first run the batch script %VSXXXCOMNTOOLS%\vsvars32.bat (where XXX is the Visual Studio version number). This script sets a few key environment variables used by the compiler (such as what to use as the include path). Using a batch script, this is insanely easy to do:

call "%VS110COMNTOOLS%\vsvars32.bat"
...
cl Foo.cpp Bar.cpp ...

since just calling a batch file from a batch script runs in the same process (and thus the added environment variables are persistent). This is what I used to do before I realized that I need more flexibility and decided to port my script to C++ which, so far, has worked wonderfully. That is, until I got to the point where I need to implement the actual compilation.

So, that's the problem I am ultimately trying to solve. The best idea I have come up with is to invoke cmd.exe /c "%VS110COMNTOOLS%\vsvars32.bat" in a separate process P3 using CreateProcess, wait for that process to terminate, and then extract the modified environment variables from that child process. That is, P1 creates P3 and waits for it to finish. P1 then sets P3's environment variables as its own. P1 then creates P2 with these environment variables set. So the code looks roughly as follows (minus all error checking):

...
CreateProcess(TEXT("cmd"), TEXT("/c \"%VS110COMNTOOLS%\vsvars32.bat\""), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);

WaitForSingleObject(pi.hProcess, INFINITE);

/* Set current process environment using pi.hProcess */
CloseHandle(pi.hProcess);

...

CreateProcess(TEXT("cl"), TEXT("..."), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);

That would be the preferred solution. I am not entirely sure if such a thing is possible, but based off my research, it appears there is a way to do this in .NET, and ProcessExplorer appears to be able to read the environment of arbitrary processes, so I would assume such a solution is possible. I just can't seem to find any documented functions that are able to get environment variables from child processes. There's also an old discussion that is similar to this on MSDN. One of the responses mentions setting Merge Environment to yes. Anyone know what that is/means? I can't seem to find any documentation on it.

If it turns out this is not possible, alternate solutions I have thought about is (1) writing a batch script that simply calls vsvars32.bat and then invokes cl.exe with the input arguments, (2) invoking cmd instead of cl with arguments to run vsvars32.bat and then compile (similar to 1, but more extensible... but not sure if possible), or (3) print the environment variables to a file and then read those in. I'd prefer not to use any of such solutions if possible.

I'm also open to alternate suggestions. Just know that 99% of what I need to do is already done, so clean, non-hacky solutions are preferred.

like image 672
Duncan Avatar asked Jan 19 '14 20:01

Duncan


Video Answer


1 Answers

The clean way to do this is to run vcvars32 to set all the environment variables and then run your process P1.

cmd /C vcvars32.bat && P1

Note that the user doesn't have to do this manually. Create a shortcut with this target:

cmd /C ""C:\Some Path\vcvarsall.bat" && start /separate C:\SomeOtherPath\YourGui.exe"

This sets the environment variables and then launches your GUI app. The start /separate stops the command-prompt lingering once the GUI app has started. If you also want this to be convenient to run from the command-line you can put it all in a batch file.

If for some reason you don't want to do this, the simplest way to get the environment variables from a batch script is to run:

cmd /U /C vcvars32.bat && set

This writes the values to standard output in Unicode. You can use a pipe to retrieve the values. This is much less hacky than trying to retrieve the variables from the memory of another process.

N.B. If you want to test this at a command-prompt you need to run:

cmd /U /C "vcvars32.bat && set"

The quotes ensure that the set runs in the child command processor.

like image 147
arx Avatar answered Oct 18 '22 18:10

arx