Search notes:

Registry: environment variables

There seem to be three places that control the values of environment variables:

Notifying the system after making changes

After changing the value of an environment variable, the system should be notified.
This can apparently be done with the WM_SETTINGCHANGE message or with rundll32.exe.

WM_SETTINGCHANGE

Notify the system by ending the WM_SETTINGCHANGE message using the WinAPI function SendMessage or SendMessageTimeout, having lParam set to the string "Environment".
With PowerShell, its possible to invoke a WinAPI function. The following script sends a message in order for new processes to have updated values for changed environment variables.
It should be noted that this broadcast message must be explicitly processed by a process. explorer.exe is the only(?) process to do so. Therefore, after sending WM_SETTINGCHANGE, a process needs to be restarted for the message to have an effect.
$funcDef = @'

    [DllImport("user32.dll", SetLastError = true, CharSet=CharSet.Auto)]

     public static extern IntPtr SendMessageTimeout (
        IntPtr     hWnd,
        uint       msg,
        UIntPtr    wParam,
        string     lParam,
        uint       fuFlags,
        uint       uTimeout,
    out UIntPtr    lpdwResult
     );

'@

$funcRef = add-type -namespace WinAPI -name functions -memberDefinition $funcDef

$HWND_BROADCAST   = [intPtr] 0xFFFF
$WM_SETTINGCHANGE =          0x001A  # Same as WM_WININICHANGE
$fuFlags          =               2  # SMTO_ABORTIFHUNG: return if receiving thread does not respond (hangs)
$timeOutMs        =            1000  # Timeout in milli seconds
$res              = [uIntPtr]::zero

# If the function succeeds, this value is non-zero.
$funcVal = [WinAPI.functions]::SendMessageTimeout($HWND_BROADCAST, $WM_SETTINGCHANGE, [UIntPtr]::zero, "Environment", $fuFlags, $timeOutMs, [ref] $res);

if ($funcVal -eq 0) {
   write-host "SendMessageTimeout did not succeed, res= $res"
}
else {
   write-host "Message sent"
}
It should be noted, however, that it's not necessary to send a WM_SETTINGCHANGE if an environment variable is changed with the PowerShell idiom to change variables because PowerShell is smart enough to figure out that this needs to be done:
PS C:\> [environment]::setEnvironmentVariable("VAR", "value", "user")
Update 2021-09-24: This assertion does not seem to be true anymore, at least when changing a user (not a system) variable (if it ever was!).
In Visual Basic for Applications, a WM_SETTINGCHANGE can be sent with the following call. The declaration of the function and constants is found here.
call SendMessageW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, byVal strPtr("Environment"))

rundll32.exe

c:\> rundll32.exe user32.dll,UpdatePerUserSystemParameters 1 True
Update 2021-09-24: This call does also not work anymore.

TODO

The Resource Kit 2003 pathman.exe promises to be able to modify environment variables. However, the RK 2003 tools are not supported on 64-bit platforms.

See also

Windows environment variables
Modifying the environment variable related entries with the PowerShell.

Index