Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"

Changing parent process environment variable

by tcf22 (Priest)
on Dec 20, 2007 at 20:59 UTC ( [id://658253] : perlquestion . print w/replies, xml ) Need Help??

tcf22 has asked for the wisdom of the Perl Monks concerning the following question:

I don't even know if this is possible, but hopefully it is. This script needs to run on Win32 and Win64. I need to run a perl script from a shell and change the environment of the shell.

I tried
system("set VAR=5");
but that has no effect on the parent as I would suspect.

I have also tried using Win32::Process::Info to get the parent PID(hopefully there is an easier way).
use Win32::Process::Info; my $pi = Win32::Process::Info->new (); my %subs = $pi->Subprocesses(); my $ParentPID; foreach(keys %subs) { foreach my $pid(@{$subs{$_}}) { if( $pid == $$ ) { print "Parent PID is $_\n"; $ParentPID = $_; } } } my @info = $pi->GetProcInfo($ParentPID); use Data::Dumper; print Dumper \@info;
but I don't know what to do with the parent PID to chenge the environment.


- Tom

Replies are listed 'Best First'.
Re: Changing parent process environment variable
by BrowserUk (Patriarch) on Dec 20, 2007 at 21:22 UTC

    You'd need to use Win32::API or Inline C to do this. See here where is says:

    To programmatically add or modify system environment variables, add them to the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment registry key, then broadcast a WM_SETTINGCHANGE message with lParam set to the string "Environment". This allows applications, such as the shell, to pick up your updates.

    No guarentees that a pre-existing shell instance would see your changes though. You might be able to inject code into the parent process to do it, but that's nasty and awkward.

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Changing parent process environment variable
by cdarke (Prior) on Dec 20, 2007 at 21:30 UTC
    It is not possible for a child process to directly alter the parent's environment block without using some shared memory magic - and that is true whatever the language used, compiled or not.

    The environment block is set at process creation time and by default is a copy of the parent's. There is no concept of a "global" environment. All this is true on Windows and UNIX.

    However on Windows, environment variables are also stored in the Registry, either in HKEY_CURRENT_USER\Environment (user variables) or HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment (system variables). So you could use one of the (several) Win32 Perl modules to change the registry.

    Now the tricky bit: for the parent to pick-up the change you should send a WM_SETTINGCHANGE message. I don't know how to do that from Perl, but there probably is a way. Look at the Win32:: namespace modules.

    There are easier ways to pass a text string between programs ;-)
      If it helps, this is how I have changed the PATH env. var. using some other weird obscure language (OK, C++). Sorry it is not in Perl, I hope this is enough for you to get the gist:
      #define ENVNAME "Path" #define MAXLEN 1024 static void UpdatePath (char *szDir) { char szPath [MAXLEN]={0}; DWORD dwReturnValue; // Get previous value if ( !GetEnvironmentVariable (ENVNAME, szPath, MAX_PATH) ) ReportError ("GetEnvironmentVariable", GetLastError()); // Update path strcat (szPath, ";"); strcat (szPath, szDir); // On W2K we need OSVERSIONINFOEX OSVERSIONINFO OsVer; OsVer.dwOSVersionInfoSize = sizeof (OsVer); GetVersionEx (&OsVer); if ( OsVer.dwPlatformId == VER_PLATFORM_WIN32_NT ) { // Write to the user environment variables - // only exists on WNT/W2K! HKEY hEnv; dwReturnValue = RegOpenKeyEx (HKEY_CURRENT_USER, "Environment", 0, KEY_SET_VALUE, &hEnv) ; if ( dwReturnValue != ERROR_SUCCESS ) ReportError ("RegOpenKeyEx", dwReturnValue); dwReturnValue = RegSetValueEx (hEnv, ENVNAME, 0, REG_SZ, (BYTE*)szPath, strlen(szPath)+1); if ( dwReturnValue != ERROR_SUCCESS ) ReportError ("RegSetValueEx", dwReturnValue); RegFlushKey (hEnv); if ( hEnv ) RegCloseKey(hEnv); } // Tell the rest of the world (cmd line) if ( !SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue) ) { ReportError ("SendMessageTimeout", GetLastError()); } // Tell our children if ( !SetEnvironmentVariable (ENVNAME, szPath) ) ReportError ("SetEnvironmentVariable", GetLastError()); } // UpdEnv
Re: Changing parent process environment variable
by ikegami (Patriarch) on Dec 20, 2007 at 21:17 UTC
    Have the child output the set commands instead of executing it, and process the commands in the parent.

    If the parent is a Perl script:

    chomp( my @cmds = `child` ); foreach my $cmd (@cmds) { next unless /^set\s+(\S.*?)=(.*)/i; $ENV{$1} = $2; }

    If the parent is a batch file:

    for /f "usebackq delims=" %%f in (`child`) do %%f

    The child would output such as

    echo set VAR=5
    print("set VAR=5\n");

    Update: Added second snippet.

Re: Changing parent process environment variable
by gamache (Friar) on Dec 20, 2007 at 21:11 UTC
    I don't know that it is possible to change the parent shell's environment variables. Why are you trying to do this?

    Perhaps another approach would be to exec a new shell with the environment you want?

      I'm not quite sure of the usefulness, but our IT department wants this for some sort of maintenance script. They say they have a compiled app that does it, but want to add more features than this app supports, so that is why I'm trying to do it.

      - Tom

Re: Changing parent process environment variable
by cdarke (Prior) on Jan 02, 2008 at 17:51 UTC
    Since the posts above I have done some further digging.

    First, the CPAN module Win32::Env has a BroadcastEnv method. However there are issues, as documented above and in the Win32::Env documentation.

    Second, I have written a module that sets env.vars using DLL injection which I will put on CPAN. /msg me if you are interested.
    I expect issues on Vista for this, and I have a fix, but I don't have access to Vista just yet. I will not upload onto CPAN until I have tested on Vista.

    There is also the possibility of getting (as well as setting) another process's env.vars. - that is an extension I am currently working on.

    I will keep this updated.
Re: Changing parent process environment variable
by cdarke (Prior) on Feb 13, 2008 at 13:11 UTC