Question

I am trying to make a simple Windows logon script that runs the vcvarsall.bat batch file to set up my Visual C++ environment. (Running Windows 7 Home Premium).

If run in an instance of cmd.exe, vcvarsall.bat sets up the environment for that cmd.exe instance. However, I want to have those environment variables set throughout my current user session.

What I would like to do is run vcvarsall.bat as a logon script. But obviously doing that alone won't persist the environment variables throughout my Windows user session. So I would really like to run vcvarsall.bat as a child process and, when it terminates, copy any environment variables from the child process that differ from the current USER environment variables.

Is there a way to get access to the child process environment using Windows scripting?

(Either using WScript.Exec(), WScript.Run(), WScript.CreateObject("WSHController").CreateScript().Execute(), or any other method???)

Was it helpful?

Solution

Here is a solution using a combination of the set command and WSH/JavaScript. The idea is to have the process running vcvarsall.bat dump its environment to the standard output (h/t @arx here) and then copy the environment variables into the "volatile" environment — i.e. the environment that lasts until the user reboots.

var shell = WScript.CreateObject("WScript.Shell")
var session_env = shell.Environment("Volatile")

// Run 'vcvarsall.bat' and then print its environment variables to standard out
// as described here: https://stackoverflow.com/a/21223991/1911388.
var vcvarsall_bat = "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/vcvarsall.bat"
var script = shell.Exec("cmd /C \"" + vcvarsall_bat + "\" && set")
var stdout = script.StdOut

// Read the environment variables from the script's standard output with method
// described here: https://groups.google.com/d/msg/microsoft.public.scripting.wsh/cwg0QudGp4Y/-wunGEk6sw0J.
// For each line that is a valid environment variable assignment of value X, set
// the variable in the user environment to X if-and-only-if it isn't already set
// to X.
do
{
    WScript.Sleep(10)
    while (! stdout.AtEndOfStream)
    {
        var line = stdout.ReadLine()
        var match = /^([^=]+)=(.*)$/.exec(line)
        if (null != match)
        {
            var varname = match[1]
            var varval  = match[2]
            var prevval = session_env(varname)
            if (prevval.toLowerCase() != varval.toLowerCase())
            {
                session_env(varname) = varval
            }
        }
    }
}
while (0 == script.Status && ! stdout.AtEndOfStream)

Setting the above as my logon script as described here, I can run cl.exe or whatever VC tool I need from any shell or Makefile directly upon logging in to Windows.

OTHER TIPS

There is no way to copy a child environment into a parent one. However, you may use WshShell.Environment object to create persistent environment variables in ways beyond setx command capabilities.

Some time ago I wrote a Batch-JScript hybrid script called SetEnv.bat:

@if (@CodeSection == @Batch) @then


:: SetEnv.bat: Creation of persistent environment variables via a JScript subroutine
:: Antonio Perez Ayala

@echo off
setlocal EnableDelayedExpansion

if "%~1" neq "" if "%~1" neq "/?" goto begin
set start=
for /F "delims=:" %%a in ('findstr /N "^</*usage>" "%~F0"') do (
   if not defined start (set start=%%a) else set /A lines=%%a-start-1
)
set line=0
for /F "skip=%start% tokens=1* delims=:" %%a in ('findstr /N "^" "%~F0"') do (
   echo(%%b
   set /A line+=1
   if !line! equ %lines% goto :EOF
)

<usage>
Create persistent environment variables.

SETENV charVar=code strVar:=string ... [/T:envtype]

  charVar    Specifies the name of one-character environment-variable.
  code       Ascii (Unicode) code of the character assigned to charVar;
             any code is valid, excepting zero.

  strVar     Specifies the name of string environment-variable.
  string     String of characters assigned to strVar;
             if contain spaces or special characters, enclose it in quotes.

  /T         Specify the environment type for the variables.
  envtype     SYSTEM    Applies to all users of the computer and is saved
                        between logoffs and restarts in the registry.
              USER      Applies to the user currently logged on to the
                        computer and is saved between logoffs and restarts.
              VOLATILE  Applies to current logon session and is not saved
                        between logoffs and restarts (this is the default).
              PROCESS   Applies to current process and might be passed to
                        child processes.

Examples:

    [call] SetEnv BEL=7 BS=8 TAB=9 CR=13 LF=10
    [call] SetEnv LastLogoff:="%date% @ %time%" /T:USER
</usage>

:begin

rem Define the variables via JScript
Cscript /nologo /E:JScript "%~F0" %*
goto :EOF


@end


// JScript section: SetEnv subprogram

// Define environment variables given in command-line arguments with this format:
//     charVar=code strVar:="string" ... [/T:SYSTEM|USER|VOLATILE|PROCESS]
// Antonio Perez Ayala

// Reference: http://technet.microsoft.com/en-us/library/ee156595.aspx

var envType = WScript.Arguments.Named.Item("T");
if ( envType == undefined ) envType = "VOLATILE";
var WshShell = WScript.CreateObject("WScript.Shell"),
    colEnvVars = WshShell.Environment(envType),
    args = WScript.Arguments.Unnamed, name_Value, equalSignPos;
for ( var i = 0; i < args.Length; i++ ) {
   name_Value = args.Item(i);
   equalSignPos = name_Value.indexOf("=");
   if ( name_Value.charAt(equalSignPos-1) == ":" )
      colEnvVars(name_Value.slice(0,equalSignPos-1)) = name_Value.slice(equalSignPos+1);
   else {
      colEnvVars(name_Value.slice(0,equalSignPos)) = String.fromCharCode(parseInt(name_Value.slice(equalSignPos+1)));
   }
}

This way, you just need to modify your vcvarsall.bat and replace the set varname=value commands by the equivalent call SetEnv varname:="value" ones; just pay attention to SetEnv syntax that require the colon-equal in string variables.

EDIT: New method as reply to the comment

@if (@CodeSection == @Batch) @then


@echo off
setlocal EnableDelayedExpansion

rem Get the list of current environment variables
set "v="
for /F "delims==" %%a in ('set') do set v[%%a]=def

rem Call vcvarsall.bat to create new variables
call "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/vcvarsall.bat"

rem Create a file with just the newly created variables and values
(for /F "tokens=1* delims==" %%a in ('set') do (
   set "v=%%a"
   if "!v:~0,2!" neq "v[" if not defined v[%%a] echo %%a=%%b
)) > vcvars.txt

rem Call JScript code to define persistent variables
Cscript /nologo /E:JScript "%~F0" < vcvars.txt
del vcvars.txt
goto :EOF


@end


var WshShell = WScript.CreateObject("WScript.Shell"),
    colEnvVars = WshShell.Environment("Volatile");
while ( ! WScript.Stdin.AtEndOfStream ) {
   var line = WScript.Stdin.ReadLine();
   var pos = line.indexOf("=");
   colEnvVars(line.slice(0,pos)) = line.slice(pos+1);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top