Access custom environment variable without rebooting (using C++) - c++

I'm writing a program using C++ that takes advantage of a custom system-wide environment variable. That variable is set by an msi installer. Later my program reads it using GetEnvironmentVariable() API.
The problem is that it seems like the system needs to be rebooted for my custom environment variable to be visible in my program and I'd hate to reboot the system just for that.
What seems to be odd is that if (without rebooting) I right-click on My Computer and then go into Properties -> Advanced and click on "Environment variables" my custom environment variable is in that list but for some reason GetEnvironmentVariable() still doesn't see it.
So is there any other API that I can use that will work without rebooting the system? (As system properties can clearly see it then.)

If you want to do this without rebooting the system you need to broadcast it. Something along the lines of
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
(LPARAM) "Environment", SMTO_ABORTIFHUNG,
5000, &dwReturnValue);
Explorer handles this message correctly so programs started after this broadcast will see the changes.
Also technically you don't need to reboot , a simple logoff and
login will suffice
Another option is to simply restart explorer

i recently encountered something like this and broadcasting the message is the correct way as explained in this kb (and by parapura):
http://support.microsoft.com/kb/104011
however, i would suggest to put _T() around the "Environment" (or maybe an 'L') to make sure you are passing in the correct string (ansi or wide). like this:
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
(LPARAM) _T("Environment"), SMTO_ABORTIFHUNG,
5000, &dwReturnValue);
i used the above in a commandline app. without the _T() the message sending succeeds but my system never seem to receive update of the environment variable.
btw, the 'setx' command line probably uses the same mechanism to update the environment variables.
also, i'm using this in an atl dll.

Related

Set current user environment variable from c++ code visible to other process like cmds

I need to set a local environment variable for current user and it shoukd be visible to other processes like a new command prompt. I need it for windows. I have tried options like putenv and editing the registry from C++ code but the new cmd prompt see the old values. Primarily i need to edit PATH variable along with few custom env variables. Will appreciate if i can get a working sample code.
Please note that the environment variable need to persist past program execution.
My requirement is for windows. I even tried running setx from C++ code and it works fine but for PATH variable it trims it down to 1024 character and i lose the update. Is there a workaround to this?
IF my wording looks confusing about the requirement. I need exactly same behavior as if i am using setx.
Thanks in advance.
If you start Cmd.exe from your process you can control its environment. The environment variables are inherited from the parent process. They can also be overridden when you call CreateProcess.
If you change the users/system environment configuration in the registry(HKCU\Environment/HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment) and log off/reboot then the first process will use these new defaults.
If you update the registry you can tell other applications to refresh their environments without logging off by broadcasting a message:
BroadcastSystemMessage(0, 0, WM_SETTINGCHANGE, 0, (LPARAM)TEXT("Environment"));
In reality it is only Explorer.exe that reacts to this message but that is enough to affect new applications started from the taskbar/start menu.
The setx command is actually an executable that sets values in the registry. If you are looking to simulate the behavior where you can set an environment variable that will last longer than the current process you will need to write it to the HKCU\Environment key. The HKCU is for the the current user and can be written to without elevated permissions.
Use RegEdit.exe or reg.exe query HKCU\Environment to view the current settings. From C/C++ you can use the Registry functions. If you can, I recommend using the ATL CRegKey class as it follows RAII and ensures handles are properly cleaned up.

Implementing custom windows authentication package

I'm building a custom authentication subpackage for MSV1_0 for Windows 7. I've used the msvsubauth sample in from the Windows SDK and I have 2 questions regarding some problems I'm facing with that:
When I'm trying just to make sure that the routine get's invoked and set the Auth0 property in the registry to my package and add a simple code at the end of the Msv1_0SubAuthenticationRoutine that creates a file:
//
// Cleanup up before returning.
//
Cleanup:
hTestFile = CreateFile(
TEXT("C:\\lsa\\lsa.txt"),
GENERIC_READ|GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if(hTestFile != INVALID_HANDLE_VALUE) {
CloseHandle(hTestFile);
}
return Status;
} // Msv1_0SubAuthenticationRoutine
Apparently the package gets invoked because when I enter my password I get an error message from windows "the parameter is incorrect" which is a good sign. But why I'm getting that error? when the exactly same code is executed from a separate .exe file it runs perfectly and creates the test text file. I've checked the permissions and set "full control" for "everyone". Any ideas? the SDK doesn't exactly mention what kind of isolation LSA is creating for code within auth packages.
The second problem is testing the AP. Currently with every change I rebuild the library, copy it to a test VM and then to the System32 folder and reboot it. Is there an easier way to do that?
Thank in advance!
Debugging in Winlogon and LSASS makes for the most time consuming debugging.
To ease your debugging, you could write a proxy AP that exports the same functions. When loaded, you proxy_ap would
Copy the real AP from a known location to a temp locationand.
LoadLibrary that DLL, GetProcAddress of everything, and forward any calls it receives to that newly loaded DLL.
Watch for changes in the directory where the original AP was copied from
When a change occurs (and if your AP changed) FreeLibrary and goto step 2
But you need to keep a tight grip on what happens on your development target, because handling the dll switch while dealing with requests comming from many threads can become a worse nightmare that what you are trying to solve.
LogonUI.exe starts a new instance every time, but LSASS.exe is long lived.
+Have a look at CVSNT source code (http://cvsnt.sourcearchive.com/). They have a pretty nice AP that implements su. Run the sample in the local system account with psexec -s (from Microsoft/SysInternals pstools suite)
Perhaps your problem is Everyone only includes Authenticated users? This is just a guess.
I suggest you use Process Monitor to monitor for Access Denied messages or for your path. It is fantastic for debugging permission/path problems of all kinds.
http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx
If you experience the issue at the "Unlock Workstation" or "change Password" screens, and it doesn't prevent you logging in, this should be easy to do - set it running, reproduce the problem, log back in and hey presto.
Otherwise you might have to resort to tricks like executing that code path only for certain user accounts, on the Nth try, etc.

Programmatically adding a directory to Windows PATH environment variable

I'm writing a Win32 DLL with a function that adds a directory to the Windows PATH environment variable (to be used in an installer).
Looking at the environment variables in Regedit or the Control Panel after the DLL has run shows me that my DLL has succeeded in adding the path to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment and HKEY_CURRENT_USER\Environment.
But when I start up a new Command Prompt (after running the DLL), the directory I added does not show up in the output of echo %PATH% and I can not access the executable that lives in that directory by typing its name.
I think my program is not doing a good job of notifying the system that the PATH has changed, or maybe it is notifying them before the change has fully taken effect. I read an article by Microsoft that says to broadcast the WM_SETTINGCHANGE message after changing an environment variable, and I am doing that with this code:
DWORD result2 = 0;
LRESULT result = SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
(LPARAM)"Environment", SMTO_ABORTIFHUNG, 5000, &result2);
if (result == 0){ /* ... Display error message to user ... */ }
The order of my calls is: RegCreateKeyEx, RegSetValueEx, RegCloseKey, SendMessageTimeout
If I press "OK" in the Control Panel "Environment Variables" window, the changes made by my DLL to the PATH show up in newly-created command prompts, so there is something that the Control Panel is doing to propagate PATH changes; I want to figure out what it is and do the same thing.
Does anyone know what I should do?
I'm running 64-bit Windows Vista but I want this to work on all Windows XP, Vista and Windows 7 operating systems.
Update: The problem with the code I posted above is that I did not put the L prefix on the "Environment" string. Although it does not say it explicitly anywhere in the Microsoft documentation that I can find, the LPARAM needs to be a pointer to a WCHAR string (2-byte characters) as opposed to a CHAR string, which is what Visual Studio's compiler generates by default when I write a string literal. The solution to my problem was to change "Environment" to L"Environment". (I thought I already tried that before posting this question, but apparently I didn't try it correctly!) But anyone who wants a complete C++ solution for this task should look at Dan Moulding's answer.
It turns out there really isn't anything new under the sun. This has already been done before, at least once. By me. I created a DLL very much like what you describe for exactly the same purpose (for use in modifying the path from an NSIS installer). It gets used by the Visual Leak Detector installer.
The DLL is called editenv.dll. The source is available at github. I just tested the installer and it updated the system PATH environment variable, no problem. Based on what you've written, I don't see anything that stands out as being wrong. I also don't see anything obvious that's missing. But it may be worth a look at the editenv.dll source (you'd be most interested in EnvVar::set() in EnvVar.cpp, and possibly the pathAdd() and pathRemove() C APIs in editenv.cpp).
I have a program which calls the same Win32 API to yours to update the environment, and it works fine.
One thing to be careful of is how you are opening up the command prompt.
If you open up the command prompt by doing this:
Start -> Run -> cmd.exe
then the environment in the prompt shows that the new variable is set.
However, I also have a programmable function key on my keyboard which I have set to run the cmd.exe process. If I open a command prompt via that function key and then type env, it doesn't show the variable as being set.
I'm not sure why it works differently, but it must have something to do with the way the cmd.exe process is launched (although both are running under my user name, not SYSTEM).
How are you opening up the command prompt?

Is there a way to set the environment path programmatically in C++ on Windows?

Is there a way to set the global windows path environment variable programmatically (C++)?
As far as I can see, putenv sets it only for the current application.
Changing directly in the registry (HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment) is also an option though I would prefer API methods if there are?
MSDN Says:
Calling SetEnvironmentVariable has no
effect on the system environment
variables. 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. Note that the
values of the environment variables
listed in this key are limited to 1024
characters.
As was pointed out earlier, to change the PATH at the machine level just change this registry entry:
HLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
But you can also set the PATH at the user level by changing this registry entry:
HKEY_CURRENT_USER\Environment\Path
And you can also set the PATH at the application level by adding the application\Path details to this registry entry:
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\
There is no API - changing the registry is the way to do it. The changed value will be used for processes starting after the change was made.
Also: Notice that running applications must actively process the settings changed message and many (most?) do not do so.
If you want to do it through the registry, you might want to look at the source code of this program.
Microsoft also provides a small command line utility called setx with its resource toolkits, which will let you do this. By the way, the regular set command just lets you define local environment variables I think.
Yes You are correct.
You also need to effect these settings without logging off
Send Message of borad casting to all windows SETTINGCHANGE for the parameter (LPARAM) "Environment" with SMTO_ABORTIFHUNG for some milliseconds(5000) using SendMessageTimeout API.
This is what setX.exe provided with resource Kit does.

How to detect launching from a "Startup"-folder shortcut?

I need to add the "Run when Windows starts" option to my program CintaNotes, but do not want to sacrifice the "cleanness" of it: it is a 100% portable freeware and should not leave traces in the system.
I've come up with the idea to autodetect running from the Startup shortcut and automatically minimizing to the system tray. Is there a way to do it? I'm using C++ and raw Winapi.
So:
- No writing to the registry
- No command line parameters
Thanks!
UPD: The question is NOT how to minimize to the system tray! The question is how can a program differentiate between being run normally and being run from a startup-folder shortcut without using registry and command-line parameters.
Your "cleanness" appears to be an artificial construct at best. If you're telling the user to create a shortcut in the start-up folder, you're already leaving a footprint (and, to be honest, there's little difference between "myprog.exe" and "myprog.exe -m"). In that case, there are some easier approaches than automagically trying to detect where you're running from.
I would simply provide a menu option in your program ("Install") which would then install the software to a fixed-disk location (as opposed to the flash drive), including the requisite Programs entry (Start, All Programs, CintaNotes).
As part of that process (or even after install), you can let them specify "Start with Windows" and then you create the start-up folder shortcut for the user with a command line option so your program can tell if it's running that way. There's no point in allowing "Start with Windows" unless the program's available (i.e., on the fixed disk, not the flash drive).
Your user need never have to worry about creating shortcuts at all, let alone ones with parameters. And this gives your program the control over how it's run - two modes, "installed" (and start minimized) or "running without installing first" (and start normal).
Something like finding the directory of the executable won't work simply because the start-up folder item that starts your program is likely to be a shortcut to it, so you won't have that path.
I think this is a classic case of asking the wrong question. In answer to your specific question, I say: no, there is no way to tell that you've been launched from a start up folder entry without some command-line parameters. But, I've been wrong before, just ask my wife :-). Some-one else may well know a way.
Adding this an an edit since comments don't allow enough space:
You ask:
What do you think of just disabling the "Start when Windows starts" option when program detects it is being run from the flash drive? I guess there's a way to detect this.
That's a good idea since it doesn't make sense to allow automatic running until it's installed (since the flash drive may not be there). One possibility:
1/ Running from flash, you start with "myprog.exe" since you just double-clicked the executable and you run in a normal window. This presents the option to "Install to fixed disk" but not "Start with Windows". As part of the install process, you may allow them to set up the installed copy to "Start with Windows" but not the current running copy.
2/ Your installed program can be set up to run as "myprog.exe -i", since you create the item in Start/AllPrograms. This would disable "Install to fixed disk" but allow you to toggle "Start with Windows". You can choose whether you want explicit running (by the user) to start normal or minimized (see (3)).
3/ The shortcut in StartUp can be "myprog.exe -s" (again, you control this because you created it). This is identical to (2) but starts minimized (if (2) started minimized anyway, there's no distinction between (2) and (3) and no reason for different command-line options).
That way, each option can have different behavior as you see fit.
Even though you have already solver the problem I just wanted to say that it's possible to detect if the program was launched from shortcut. Once you have the shortcut path you can compare it to startup folder.
See the section "Detect if an executable was started from a Short-Cut
" at Undocumented CreateProcess
Check the registry for this key.
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run and add a new String key with the path of your application as value. and use NOTIFYICONDATA structure for minimizing your application to the tray.
Why don't you use an argument to start the application minimized like:
YourProgram.exe -m