为什么"%~fI"参数扩展能够"访问"不存在的驱动器?

npo*_*aka 8 windows cmd batch-file

我正在使用以下命令:

C:\>for %I in (a: b: c: ">:" "&:") do @rem %~fI
C:\>pushd c:
C:\>set "
Run Code Online (Sandbox Code Playgroud)

和输出:

=:=&:\

=>:=>:\

= A:= A:\

= B:= B:\

= C:= C:\

....

由于=Drive:变量存储最后访问的路径相应的驱动器,看起来%~fI扩展以某种方式访问​​不存在的驱动器(这是不可能的).(所有参数扩展都会创建这样的变量)

MC *_* ND 3

当在可替换参数中使用修饰符for来请求路径元素时,for命令(好吧,一个检索正在读取的变量内容的函数)使用该GetFullPathName函数将输入字符串调整为可以处理的内容。当请求相对路径时,此 API 函数(以及此 API 调用的一些操作系统基本函数)会生成指示的行为。您可以测试此 C 代码(抱歉,只是一个快速代码测试),使用 ex 调用可执行文件。;:作为第一个参数。

#define _WIN32_WINNT   0x0500
#include <windows.h>
#include <stdio.h>

#define BUFFER_SIZE 4096

int main(int argc, char **argv){

    char buffer[BUFFER_SIZE];
    DWORD ret;

    LPTSTR lpszVariable; 
    LPTCH lpvEnv; 

    if (argc != 2) return 1;

    if (0 == GetFullPathName( argv[1], BUFFER_SIZE, buffer, NULL )){
        printf ("GetFullPathName failed (%d)\n", GetLastError());
        return 2;
    }

    printf("current active directory: %s\r\n", buffer );

    if (NULL == (lpvEnv = GetEnvironmentStrings())) { 
        printf("GetEnvironmentStrings failed (%d)\n", GetLastError()); 
        return 3;
    }

    lpszVariable = (LPTSTR) lpvEnv;
    while (*lpszVariable) {
        if (lpszVariable[0]== '=') printf("%s\n", lpszVariable);
        lpszVariable += lstrlen(lpszVariable) + 1;
    }
    FreeEnvironmentStrings(lpvEnv);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

得到类似的东西

D:\>test ;:
current active directory: ;:\
=;:=;:\
=C:=C:\Windows\System32
=D:=D:\
=ExitCode=00000000
Run Code Online (Sandbox Code Playgroud)

2016/12/23编辑

这是针对 Windows 10 的,但由于 Windows 7 的行为相同,因此它应该共享相同或相似的代码。

环境字符串到控制台的输出由DisplayEnvVariable函数处理。在较旧的 Windows 版本中(已检查且 XP 是这样做的),此函数调用GetEnvironmentStrings来检索值,但现在(已检查且在 Vista 中已更改)使用指向内存区域的指针。不知何故(抱歉,目前我不能给这个问题更多的时间),它指向环境的未更新副本(在这种情况下,更新不是由命令生成的,而是从解析当前驱动器时调用的cmd基本函数生成的)Rtl路径),生成观察到的行为。

不必执行pushdcd命令,对环境的任何更改或任何进程创建都将导致指针的更新。

D:\>test ;:
current active directory: ;:\
=;:=;:\
=C:=C:\Windows\System32
=D:=D:\
=ExitCode=00000000
Run Code Online (Sandbox Code Playgroud)

您可以more用简单的代码替换该行set "thisIsNotSet="以获得相同的结果