Why can't I access a variable named __CD__ in Windows 7?

Note. This question is based on the behavior observed in Windows 7. I believe that the behavior applies to all other versions of Vista. Based on MC ND's answer and Foxidrive comments, this does not apply to XP.

I can use the %CD% pseudo-dynamic variable to get the current directory without the final \ . If I define a true variable called CD using set "CD=someValue" , then %CD% returns the value assigned to me, not the current directory.

The lesser-known dynamic variable is %__CD__% , which is the same as %CD% , except that it includes a trailing \ . But if I do set "__CD__=someValue" , I cannot access the value assigned to me! I can use set __CD__ to see that my variable exists with my assigned value, but %__CD__% always returns the current directory!

Does anyone know how this mechanism of %__CD__% works (why is it different from any other dynamic variable)?

Can anyone come up with a way to access my __CD__ variable using only standard command commands (indiscriminately set __CD__ )?

This is an academic issue rather than a practical one. No one should define variables with names that correspond to dynamic variables.




The reason I don’t want a routine that parses the results of set __CD__ is because they can contain strings. Someone could define two variables, __CD__ and __CD__2 , or they could define a single __CD__ value containing the feed of the string, followed by __CD__2=... It would be impossible to distinguish between the two scenarios. (As I said, this is an academic question, not a practical one!)

+10
windows-7 environment-variables batch-file
Nov 22 '13 at 23:38
source share
5 answers

I have a theory about how and why __CD__ behaves in a different way than any other environment variable.

I wrote a simple ENV.JS script to test the process environment.

 var env=WScript.CreateObject("WScript.Shell").Environment("Process"); WScript.echo(env(WScript.Arguments.Item(0))); 

ENV.JS expects the name of the environment variable as a single argument and simply prints the value of the variable. I tested on a computer with Windows 7. The script can be launched from the CMD.EXE console, or it can be launched directly using the shortcut that defines the argument.

I will classify various dynamic variables, showing contrasting behavior, as well as a theory regarding the mechanism by which they work.

There are a number of dynamic variables that can be divided into three classes:

1) Normal "variables" without prefix or suffix

 CD current directory DATE current date TIME current time ERRORLEVEL current errorlevel RANDOM random integer between 0 and 32767 CMDEXTVERSION current extension level (only available if extensions are enabled) CMDCMDLINE the command line that invoked the current CMD.EXE level 

Dynamic values ​​are available only through the extension in CMD.EXE and only when command extensions are enabled. They are not available through SET, or ENV.JS. Dynamic values ​​can be overridden by explicitly defining a static value using SET. Override values ​​are available through SET and ENV.JS.

 C:\test>echo %cmdcmdline% "C:\Windows\system32\cmd.exe" C:\test>set cmdcmdline Environment variable cmdcmdline not defined C:\test>cscript //nologo env.js cmdcmdline cmdcmdline= C:\test>set cmdcmdline=override C:\test>echo %cmdcmdline% override C:\test>set cmdcmdline cmdcmdline=override C:\test>cscript //nologo env.js cmdcmdline cmdcmdline=override C:\test>cmd /e:off Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. C:\test>echo %cmdcmdline% %cmdcmdline% 

Dynamic values ​​are not environment variables at all, so they are not available for SET or ENV.JS. The code of the extension variable in CMD.EXE first checks the process environment variables for the variable, and only if it is not found, it compares this name with a list of special dynamic names. For each dynamic "variable", a special CMD.EXE code must exist to obtain a value from the corresponding source.

The behavior of this class of dynamic variables is described by Microsoft Raymond Chen on his blog - The Old New Thing

2) Dynamic variables with a prefix =

 =ExitCode =ExitCodeAscii =C: =D: ... =Z: 

Each of these dynamic values ​​is undefined until any command that defines it is executed. For example, a new CMD.EXE console session starts with = ExitCode undefined. It becomes detectable after executing an external command that sets the return code.

There is one exception, because the variable ={driveLetter}: corresponding to the current directory will always be determined even when CMD.EXE is launched first.

You cannot use SET to define any of these variables, because SET does not allow = in the variable name. But the main variable space of the process environment allows = in variable names. This needs to be done only outside the context of CMD.EXE.

I wrote an additional TEST.JS to check these variables:

 var shell=WScript.CreateObject("WScript.Shell"); var env=shell.Environment("Process"); WScript.echo('Within JScript: ExitCode='+env("=ExitCode")); env("=ExitCode") = "override"; WScript.echo('Within JScript after override: ExitCode='+env("=ExitCode")); WScript.echo('Within JScript test.bat return code = '+shell.run("cmd /c test.bat",10,1)); WScript.echo('Within JScript after test.bat: ExitCode='+env("=ExitCode")); 

In turn, TEST.JS calls TEST.BAT:

 @echo off echo Within test.bat: ExitCode=%=ExitCode% cmd /c exit 10 echo =Within test.bat: ExitCode=%=ExitCode% pause exit %errorlevel% 

Below are some test results using =ExitCode , starting with a new CMD.EXE session:

 Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. C:\test>echo %=ExitCode% %=ExitCode% C:\test>set "=ExitCode=override" The syntax of the command is incorrect. C:\test>cscript //nologo env.js =ExitCode =ExitCode= C:\test>REM cscript is an external command that set the value! C:\test>echo %=ExitCode% 00000000 C:\test>set = The syntax of the command is incorrect. C:\test>set ""|findstr /b =ExitCode =ExitCode=00000000 C:\test>cscript //nologo env.js =ExitCode =ExitCode=00000000 C:\test>cscript //nologo test.js Within JScript: ExitCode=00000000 Within JScript after override: ExitCode=override Within JScript test.bat return code = 10 Within JScript after test.bat: ExitCode=override C:\test> 

Here are the results of TEST.BAT that TEST.JS launched in a new window:

 Within test.bat: ExitCode=override Within test.bat: ExitCode=0000000A Press any key to continue . . . 

I believe that these dynamic variables are true environment variables, so both SET and JScript can access them. (SET can only access a value using the special SET "" syntax.) Variables are dynamically determined (or updated) by CMD.EXE each time the relavent command is executed. ENV.JS and TEST.JS can see the value set by the calling CMD.EXE session. In the TEST.BAT cmd session, you could see the inherited override value set by TEST.JS. But JScript continued to get the override value after the TEST.BAT exit because CMD.EXE was not there to update the value with the return code.

This class of dynamic variables is available whether command extensions are enabled or disabled. Dynamic values ​​are retained even if extensions are disabled, as shown below:

 C:\test>cmd /e:off Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. C:\test>echo %=c:% C:\test C:\test>cd test C:\test\test>echo %=c:% C:\test\test 

3) __CD__ - A special case for everyone!
EDIT - In fact, __APPDIR__ works the same way.

This dynamic variable is always available for both CMD.EXE and JScript. An override value can be specified, but neither CMD.EXE nor JScript can see the override value, except that the SET command can display an override value. (Also not shown, but jeb discovered that SET / A can read an override value if it is numeric).

I wrote another TEST2.JS to test this variable.

 var shell=WScript.CreateObject("WScript.Shell"); var env=shell.Environment("Process"); WScript.echo('Within JScript: __CD__='+env("__CD__")); env("__CD__") = "JS override"; WScript.echo('Within JScript after override: __CD__='+env("__CD__")); shell.run('cmd /c "set __CD__&pause",1,0'); 

The following are some test results:

 Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. C:\test>echo %__CD__% C:\test\ C:\test>set __CD__ Environment variable __CD__ not defined C:\test>set "__CD__=Batch override" C:\test>echo %__CD__% C:\test\ C:\test>set __CD__ __CD__=Batch override C:\test>cscript //nologo test2.js Within JScript: __CD__=C:\test\ Within JScript after override: __CD__=C:\test\ C:\test> 

Here is the result of the CMD.EXE window opened by TEST2.JS:

 __CD__=JS override Press any key to continue . . . 

If I define a shortcut for ENV.JS as follows:

 Target: C:\test\env.js __CD__ Start in: C:\test\xyz 

Then, when I click on it, I get a warning window:

 __CD__=C:\test\xyz\ 

I find these results fascinating. Dynamic value should not be a true environment variable. Presumably, there is a low-level OS environment access routine that automatically returns the current directory of the process whenever it is prompted to return the __CD__ value. It does this even if a true static environment variable named __CD__ .

The CMD.EXE SET command must access an environment variable that is different from most other contexts. I assume that the C program can be written to get a pointer to the memory of the process environment and __CD__ any true user-specified __CD__ value, as well as the SET command.

Given that this is a low operating system that provides this value, it is not surprising that %__CD__% is available even when command extensions are disabled.

 C:\test>cmd /e:off Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. C:\test>echo %__cd__% C:\test\ C:\test>cd test C:\test\test>echo %__cd__% C:\test\test\ 

I assume that the concept of the current directory is critical for each process from an OS perspective, and MS decided to give the process access to the value using a dynamic virtual environment variable. XP OS allows you to extend any overridden __CD__ value. Perhaps this caused problems for some applications, and MS decided to change the environment access procedures (starting from Vista, maybe?), To always return the real current directory, regardless of any custom __CD__ variable.

So, based on the theory that this is a low-level procedure that returns the current directory, now I believe that it is impossible to use your own command commands to reliably get any custom value for __CD__ .

+13
Nov 23 '13 at 23:29
source share

[This should be a comment, but it would be very big!]

I would like to add that% CD% variable is also a special case for the following reason: if the setlocal command is executed and then the current directory is changed, the back endlocal command changes the current directory back to one active directory when setlocal executed:

 @echo off echo Original: %CD% setlocal md newdir cd newdir echo In newdir: %CD% endlocal echo After endlocal: %CD% 

This behavior indicates that the dynamic variable% CD% is saved using setlocal and restored with (explicit or implicit) endlocal ', similar to normal variables. However, the strange thing is that this mechanism works even if the user-defined variable CD is defined before or after setlocal ! The conclusion is that the setlocal command saves the current directory in an area separate from environment variables, and that the endlocal command restores the current directory from this area.

+3
May 12 '14 at 14:50
source share

According to this link, there are read-only environment variables on the command line that include __CD__.

From this page:

Undocumented dynamic variables (read-only)

% __ CD __% Current directory with a trailing backslash.

% = C:% Current directory of drive C :.

% = D:% Current directory of drive D: if drive D: was available in the current CMD session.

% = ExitCode% The hexadecimal value of the last return code set by EXIT / B

% = ExitCodeAscii% The ASCII value of the last return code set by EXIT / B if it is greater than 32.

+2
Nov 22
source share

I see no reason why userdefined __cd__ is different from the user variable cd .
But I did some tests.

 setlocal EnableDelayedExpansion echo #0 %__cd__% if defined __cd__ echo #1 It defined set __cd__=1234 echo #2 %__cd__% echo #3 !__cd__! call echo #4 %%__cd__%% echo #5 %__cd__:x=y% echo #6 !__cd__:x=y! if defined __cd__ echo #7 It defined set __cd__ set /a result=__cd__ echo #7 %result% 

So, there are three interesting points.

  • Test with if defined always positive
  • set can display a user variable
  • set /a can access a custom variable
+2
Nov 23 '13 at 8:53
source share

Just for completion - and a way to get the correct value of a variable: use the / I parameter of the start command to generate a new cmd with the original environment of the current process without a changed variable.

 D:\Sandbox>ver Microsoft Windows XP [Versión 5.1.2600] D:\Sandbox>echo %__cd__% D:\Sandbox\ D:\Sandbox>set __cd__=hello D:\Sandbox>echo %__cd__% hello D:\Sandbox>start /b /wait /i cmd /q /v:on /c "echo !__cd__!" D:\Sandbox\ D:\Sandbox>echo %__cd__% hello D:\Sandbox>set __cd__= D:\Sandbox>echo %__cd__% D:\Sandbox\ D:\Sandbox> 
+2
Nov 23 '13 at 9:01
source share



All Articles