Nathan Wild 3 Posted September 11, 2019 This can not possibly be as complicated as it seems... I want to set an environment variable from within my application that persists after it terminates. I have tried using SetEnvironmentVariable() and then SendMessage() to broadcast the change, to no avail... I found an old post with the following function which seems like it SHOULD do what I want - but no matter what my environment is not touched. function SetGlobalEnvironment(const Name, Value: string; const User: Boolean = True): Boolean; resourcestring REG_MACHINE_LOCATION = 'System\CurrentControlSet\Control\Session Manager\Environment'; REG_USER_LOCATION = 'Environment'; begin with TRegistry.Create do try if User then { User Environment Variable } Result := OpenKey(REG_USER_LOCATION, True) else { System Environment Variable } begin RootKey := HKEY_LOCAL_MACHINE; Result := OpenKey(REG_MACHINE_LOCATION, True); end; if Result then begin WriteString(Name, Value); { Write Registry for Global Environment } { Update Current Process Environment Variable } SetEnvironmentVariable(PChar(Name), PChar(Value)); { Send Message To All Top Window for Refresh } SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, Integer(PChar('Environment'))); end; finally Free; end; end; Any help appreciated! Share this post Link to post
Stéphane Wierzbicki 45 Posted September 11, 2019 Does it help? https://stackoverflow.com/questions/37593507/how-can-i-change-an-environment-variable-and-have-other-apps-see-the-value-witho Share this post Link to post
David Heffernan 2345 Posted September 11, 2019 You code runs the risk of hanging when it broadcasts. But the basic concept is correct albeit poorly implemented. Have you done any debugging? Share this post Link to post
Dave Nottage 557 Posted September 11, 2019 Additional info here: https://stackoverflow.com/a/3637349/3164070 Share this post Link to post
Nathan Wild 3 Posted September 12, 2019 Stéphane Wierzbicki: That post was helpful, but seems to be pretty much what I am doing? I changed my code to use SendMessageTimeOut(), which seems to make all the top level windows that I have open flash a little, but seems to leave my live environment otherwise untouched 😞 Dave Nottage: That too was very helpful, although all it really told me is that I am fighting a losing battle 😉 Share this post Link to post
David Heffernan 2345 Posted September 12, 2019 Did you do any debugging? Did you check that the registry was modified as intended? Reference back to https://docs.microsoft.com/en-gb/windows/win32/procthread/environment-variables Share this post Link to post
aehimself 396 Posted September 12, 2019 Also please note that in Windows several environmental variables requires a logoff-logon for the changes to take affect (like %PATH%). Share this post Link to post
David Heffernan 2345 Posted September 12, 2019 5 hours ago, aehimself said: Also please note that in Windows several environmental variables requires a logoff-logon for the changes to take affect (like %PATH%). Don't think that's true. That's the point of the broadcast message. The shell updates its environment and uses that when creating new processes. Share this post Link to post
Nathan Wild 3 Posted September 12, 2019 3 hours ago, David Heffernan said: Don't think that's true. That's the point of the broadcast message. The shell updates its environment and uses that when creating new processes. This is correct... If I run my process to set the variable, then spawn a new shell it works... So it seems like it's just the broadcasting part of this process that doesn't work? Share this post Link to post
Remy Lebeau 1396 Posted September 17, 2019 On 9/11/2019 at 12:17 PM, Nathan Wild said: I have tried using SetEnvironmentVariable() and then SendMessage() to broadcast the change, to no avail... I found an old post with the following function which seems like it SHOULD do what I want - but no matter what my environment is not touched. New values are not actually saved in the Registry until the key is closed, which you are not doing until AFTER sending the broadcast. And the TRegistry.LazyWrite property is true by default, making TRegistry.CloseKey() essentially asynchronous, so new values are saved in the background while control returns to your app. It may take a few seconds for the new values to actually get written, flushed to disk, and cached, thus other apps are likely to read old values while processing the broadcast. You should set LazyWrite=false to ensure the new value is fully saved when closing the key, and you should close the key before sending the broadcast. Try this: function SetGlobalEnvironment(const Name, Value: string; const User: Boolean = True): Boolean; const REG_MACHINE_LOCATION = 'System\CurrentControlSet\Control\Session Manager\Environment'; REG_USER_LOCATION = 'Environment'; var dwReturnValue: DWORD_PTR; begin with TRegistry.Create do try LazyWrite := false; if User then begin { User Environment Variable } RootKey := HKEY_CURRENT_USER; Result := OpenKey(REG_USER_LOCATION, True); end else begin { System Environment Variable } RootKey := HKEY_LOCAL_MACHINE; Result := OpenKey(REG_MACHINE_LOCATION, True); end; if not Result then Exit; WriteString(Name, Value); { Write Registry for Global Environment } finally Free; end; { Update Current Process Environment Variable } SetEnvironmentVariable(PChar(Name), PChar(Value)); { Send Message To All Top Windows for Refresh } //SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, LPARAM(PChar('Environment'))); SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, LPARAM(PChar('Environment')), SMTO_ABORTIFHUNG, 5000, @dwReturnValue); end; Share this post Link to post
aehimself 396 Posted September 23, 2019 On 9/12/2019 at 1:31 PM, David Heffernan said: Don't think that's true. That's the point of the broadcast message. The shell updates its environment and uses that when creating new processes. I just checked my old codes and yes - they do take effect after the broadcast was sent. Must have mistaken it with setting it somewhere else in Windows...? Share this post Link to post
Nathan Wild 3 Posted October 2, 2019 Remy: Thanks very much! With your code, I can see all top level windows flicker a little when the broadcast call is issued, but my shell still does not recognize the new variable even after the process terminates. Closing and opening a new shell does work, but this is not what I'm after... Starting to thing that this is not possible with Windows?! Share this post Link to post
David Heffernan 2345 Posted October 2, 2019 What you are trying to do is possible. I guess there is just a mistake somewhere, or some extra detail that we don't know of yet. 1 Share this post Link to post
Remy Lebeau 1396 Posted October 2, 2019 14 hours ago, Nathan Wild said: With your code, I can see all top level windows flicker a little when the broadcast call is issued, but my shell still does not recognize the new variable even after the process terminates. Which specific environment variable are you trying to set? Share this post Link to post