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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With