Windows Confidential: The hidden variables
Embedding one environment variable within another is simply a matter of good operational timing.
There’s a dialog box hidden deep within the Control Panel called Environment Variables. To find it, you’ll need to navigate to the System Properties control panel. Then stand on one foot, go to the Advanced tab, and click the Environment Variables button while reciting the alphabet backward.
OK, perhaps a couple of those steps aren’t actually necessary. What is clear, though, is that it wasn’t intended for average users to be able to easily find the Environment Variables dialog box, much less understand what it does.
When the system builds an environment for a new user, it consults the settings established in the dialog box to determine which variables to place into the environment and with what values. The general principle is that if an environment variable is set in both the User and System sections, then it will follow the User definition.
Instead of completely ignoring the System variable, though, the system can use it to help define the User variable. People often want the User environment variable to be based on the System environment variable. For example, they may want the User PATH to consist of the System PATH, plus some additional directories.
Here’s how the environment-building process works. It proceeds in roughly four steps:
- First, the system creates some predefined machine-wide environment variables, such as SystemRoot and ALLUSERSPROFILE (but not COMPUTERNAME or ProgramFiles).
- Second, it creates environment variables from the System section of the Environment Variables dialog box. The System environment variable definitions can use the “%” notation to refer to the predefined environment variables created in the previous step. For example, you can set a System environment variable to %SystemDrive%\Extras. After the System environment is complete, Windows starts building the User environment.
- Step three is to create predefined per-user environment variables, such as USERPROFILE and APPDATA. The COMPUTERNAME and ProgramFiles-related variables are also created here, even though they’re technically System variables and not per-user variables.
- Finally, the system creates the environment variables. These are in the User section of the Environment Variables dialog box and have access to any variables created by the first three steps, so you can set a User environment variable to %USERPROFILE%\Extras or a custom System environment variable set in the second step. If a User environment variable has the same name as a System environment variable, the new value replaces the old.
The replacement happens after the system computes the new value, so you can do things like have a User PATH environment defined as %PATH%;C:\Extras. The %PATH% variable refers to the System version of the variable. Now that you see what works, you can figure out what doesn’t work. It’s the stuff that’s not described in the previous section as working.
For example, you can use %SystemRoot% anywhere. You can use any System environment variable to help define a User environment variable. You can use a predefined per-user environment variable to help define a User environment variable. But you can’t use a per-user environment variable to help define a System environment variable. Time does not go backward.
One customer was having difficulty setting the System PATH environment variable to %APPDATA%;C:\Windows. They found the final environment merely contained the literal path as specified (percent signs and all), instead of replacing it with the value of the APPDATA environment variable. If you look through the sequence of operations previously detailed, it’s clear why this occurred. They were trying to set a System environment variable based on a variable that had not yet been defined.
The solution was simple: Move editing the PATH from the System environment list box to the User environment list box. That way, when it wanted to use the %APPDATA% environment variable, the variable would be there.
The code that builds the User environment could have tried to build a fancy dependency graph. Then it could have added a custom syntax that allowed variable definitions to declare whether a particular occurrence of %PATH% was intended to refer to the System or User version of the PATH variable. Then it could have collected all that information, built a dependency graph, and carefully defined the variables in an order so that each variable was defined before its dependents. Then you would have to decide what to do if a circular reference was detected.
The goal of the environment is not to emulate a spreadsheet-recalculation engine. It just wants to create an environment block from some basic information, and choosing simple rules makes the entire process easier to explain and understand. Is that so hard to understand?
Raymond Chen's Web site, The Old New Thing, and identically titled book (Addison-Wesley, 2007) deal with Windows history and Win32 programming. He does not feed the animals outside of designated areas.