Get Environment Variables of Child Process in VC++ - windows

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.

Do not retrieve and set environment variables across process boundaries. Run vsvars32.bat and cl.exe in the same process, like they are meant to be. cmd.exe lets you execute multiple commands at one time using its && operator:
cmd.exe /c "\"%VS110COMNTOOLS%\vsvars32.bat\" && cl ..."

Related

Is there a way to close terminal after calling sysExecCmd() in CAPL script?

I wrote a CAPL script that calls a python script using sysExecCmd like sysExecCmd("python",myParameters,myTree), where myTree is the current (nested) working tree wrt the current directory the CAPL resides in and myParameters are generated at runtime and take care of certain variables.
Problem is: every time sysExecCmd is called, it opens a new terminal and each one is persistent. I've tried to call a exit command without parameters in the same myTree like sysExecCmd("exit","",myTree), but it didn't work (and I didn't had many hopes it should have).
While the solution to the problem is not the central point here (I could do with a workaround and either use a batch or delegate to python to close the terminal somehow), I would like to know if there is a way to close terminals on the fly using CAPL scripts.
Usually, for me it works in this way:
sysExecCmd("C:\\Projects\\CANoe\\some_script_to_execute && exit","");
&& exit does not always work.
You could also do this with the /c option.
/c Carries out the command specified by string and then stops.
Note that I am using sysExec and not sysExecCmd as the latter executes cmd with the /k option:
/k Carries out the command specified by string and continues.
snprintf(parameters, elcount(parameters), "/c mkdir %s", directoryName);
result = sysExec("cmd.exe", parameters, workingPath);

CMake convert unix to windows path

I'm trying to convert a unix style MSYS path such as
/c/my/path/to/a/folder
to a Windows path, or something that CMake would understand,
e.g C:/my/path/to/a/folder.
I'd like it to work on a path that is already correct.
Is there any proper way to do it ?
Note : Please do not mention cygwin's cygpath.
Edit: file(TO_CMAKE_PATH mypath result) is not working
There's no built-in CMake functionality for this, but you can write a function/macro to do it:
macro(msys_to_cmake_path MsysPath ResultingPath)
string(REGEX REPLACE "^/([a-zA-Z])/" "\\1:/" ${ResultingPath} "${MsysPath}")
endmacro()
set(mypath "/c/my/path/to/a/folder")
msys_to_cmake_path(${mypath} result)
message("Converted \"${mypath}\" to \"${result}\".")
Having said that, I agree with Antonio's comment in that it seems unusual to need this in the first place.
As an alternative to the accepted answer, you may wish to consider that MSYS itself will perform the conversion at any boundary between MSYS parent and native child process; thus, in an MSYS shell console:
cmd //c echo /c/my/path/to/a/folder
would display the appropriately converted path c:/my/path/to/a/folder. Additionally, this technique offers the possible advantage that it will emit the fully converted native form of a path, such as:
cmd //c echo /home/my/path/to/a/folder
to yield its native equivalent C:/MinGW/msys/1.0/home/my/path/to/a/folder, (assuming your MSYS installation is in the recommended default location, at C:/MinGW/msys/1.0).
With the caveat that running MSYS shell without proper initialization, as performed by msys.bat, may not work, (especially when running on 64-bit Windows), you may be able to run an equivalent command from within a native process, (such as within CMake), as:
C:/MinGW/msys/1.0/bin/sh -c 'cmd //c echo /home/my/path/to/a/folder'
Note that, if you invoke this from a native process which is itself running within an MSYS console, the initialization will have been correctly performed for the console's own shell process, and should thus propagate through the native process; the issues are more likely to arise if you attempt to invoke MSYS processes directly from a cmd.exe process, in a native Windows console, (or other native container).
Also note that, if the path name in question contains spaces, (never a good idea), you may need to enclose it within double quotes:
cmd //c echo "/home/my/path with spaces"
In this case, some experimentation indicates that the double quotes remain within the cmd output. I'm not entirely certain if this is necessary; you should use your discretion in your own particular usage case.

Is it possible to change environment variables within Batch and utilize them without restarting CMD prompt?

I want to setup a system wide environment variable within my batch script script running within the CMD prompt, I have been able to achieve this by calling:
setx MyEnvVar "C:\<Some Path>" /M
However when I do:
echo "MyEnvVar is %MyEnvVar%"
afterwards the statement that outputs at prompt is "MyEnvVar is" although the variable has been setup with setx and I can observe it through looking at the Windows - system properties - environment variables GUI.
I know this is because the CMD prompt has to be restarted for it to pick up the new environment variables however I don't want to stop the execution of my batch script and tell the user to manually restart the CMD prompt window and re-run my script so the environment variables are picked up. Is there some other way of getting around this?
It would be better if I could get around this without utilizing the "call" method and breaking the script to two segments
batchfileA - Code up until and including the call to change the environment variables thereafter utilize call method to call batchfileB
batchfileB- The rest of the original code placed inside batchfileB and called with call method within batchfileA
I had tried using set after the setx and echoed the result and the variable was null so I assumed that the value was not taking because I had to restart the CMD prompt what I forgot was I had enabled DelayedExpansion and had to use ! (exclamation marks) instead of % (percent) signs around my variable names

Install MathLink program with arbitrary PATH environment

Is it possible to use Install[] to start a MathLink program with a custom PATH environment variable?
I am trying to use mEngine to connect Mathematica to MATLAB on Windows. It only works if mEngine.exe is launched when the PATH environment variable includes the path to the MATLAB libraries. Is it possible to modify the PATH for launching this program only, without needing to modify the system path? Or is there another way to launch mEngine.exe?
#acl's solution to wrap mEngine.exe in a batch file, and temporarily modify the PATH from there, works correctly:
I used this as the contents of mEngine.bat:
set PATH=c:\path\to\matlab\bin\win32;%PATH%
start mEngine.exe %*
*% ensures that all command line arguments are passed on to mEngine.exe
start is necessary to prevent the command window from staying open until mEngine.exe terminates
It can be started using Install["mEngine.bat"].
Since all the information that is needed for the kernel to communicate with mEngine.exe is passed by Install[] as command line arguments, all we need to do is launch mEngine.exe with these arguments. It is not necessary for Install[] to know the location of mEngine.exe, the important thing is that the process gets launched with the correct command line arguments, which is ensured by %*.

Preprocessor to add functionality to Windows's CMD?

I need to do a fair bit of scripting in my job as a SQL Server DBA. Sometimes, I need to deploy a fix script to a very restricted environment, where the only option for scripting may be DOS Batch. In one such environment, even VBScript/WSH isn't a possibility, let alone PowerShell. Anyone who has written enough batch files on DOS and Windows knows that it's very limited and a huge PIA when you need to do anything too complicated. This is especially true for folks who have worked with Unix shell scripting, Perl, Tcl, Python, Ruby, etc.
A possible solution to this would be a CMD preprocessor that would add some of the useful functionality from more capable scripting languages. I've tried to find such a utility, but so far I've had no luck.
Which finally leads to my question: is anyone aware of a such a CMD preprocessor? If not, what functionality would you like to see in one?
Addendum:
If you're unfamiliar with the idea of a preprocessor see this Wikipedia entry.
To clarify, I'm thinking of a tool that would add features like:
Functions
Backtick (`) ala Unix shell
...and possibly others. Those are two features I've wished CMD had and can think of a way to implement them with a CMD preprocessor. Functions could be implemented with env vars and GOTO/labels; backticks by piping to a temp file and using set /p =< to read in the result to an env var.
You can already achieve these same ends, but it gets to be very tedious and verbose- which is how I came to the idea of having a preprocessor handle the boilerplate for features like those.
Example
Using the example of backticks, here is an example of unprocessed code from my hypothetical Batch++ and processed vanilla batch script, ready to be run by CMD.exe:
Batch++ Source (test.batpp)
copy `dir /b /s c:\ | find "CADR-README.htm"` \\srv01\users
Run it through the preprocessor
bpp test.batpp > post_test.bat
Resulting CMD/BAT code (post_test.bat)
dir /b /s c:\ | find "CADR-README.htm" > _bt001.tmp
set /p _BT001 =< _bt001.tmp
copy %_BT001% \\srv01\users
set _BT001=
del _bt001.tmp
I am not sure to interpret correctly your question. If you run in a controlled environment that doesn't allow you to run any scripting extension, how are you going to access such a preprocessor?
However, and in regard of the two features you request, you are OK with .BATs. Both features are supported by BAT processing in current Windows versions.
Functions: you have the extended CALL syntax, that supports parameter passing thru argument references %1 .. %9, and enhanced with expansion substitution using %~ syntax. Read HELP CALL.
Backtick: not sure what you want but, in the FOR /F command you may pass a backticked string to be run and its output captured. Read HELP FOR.

Resources