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!).
call SendMessageW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, byVal strPtr("Environment"))