@
hez2010 #9 现象和规范是两码事,单纯做一个实验就认为 Windows 支持超长变量无异于依赖于 C++ undefined behavior 编程。
应该注意 Win32 环境变量有两个存在的形式:在注册表里存储的系统/用户级变量、在每个进程里存储的进程级变量。
考虑下面的每个实验,每个实验开始之前都不存在名字叫做 verylong 的系统、用户、进程级环境变量。
————
实验 1:在 PowerShell 里运行
[System.Environment]::SetEnvironmentVariable('verylong', '1'*40000, 'User')
然后通过 explorer 打开一个新的 PowerShell ,并在新的 PowerShell 里运行
$env:verylong.Length
在 Windows 10 (build 19045.4651) x64 里得到的结果是 4095 ,这说明 explorer.exe 把 %verylong% 截断为 4095 个字符了。
————
实验 2:在 PowerShell 里运行
[System.Environment]::SetEnvironmentVariable('verylong', '1'*40000, 'User')
$cred = Get-Credential
Start-Process powershell -cred $cred -arg '-Command start powershell'
输入当前用户的用户名和密码,然后等待新的 PowerShell 启动(注意这里需要两次跳转,似乎有不知名的 bug 导致单纯打开一次 PowerShell 无法输入),在新的 PowerShell 里面运行
$env:verylong.Length
得到 40000 ,这说明“什么都不做”似乎是可以自动读取超长环境变量,然而……
————
实验 3:在 PowerShell 里运行
[System.Environment]::SetEnvironmentVariable('verylong', '1'*40000) # 默认的范围是进程
会得到
Exception calling "SetEnvironmentVariable" with "2" argument(s): "Environment
variable name or value is too long."
查阅 PowerShell 的源代码,发现这个异常是从 Win32 API SetEnvironmentVariable
https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setenvironmentvariable 来的(而不是 PowerShell 自己检查的),文档明确指出超过 32767 个字符是不支持的,实际调用也会返回 FALSE 并用 GetLastError 指示变量过长。
————
实验 2 、3 表明实际上超过 32767 个字符的环境变量是不支持的,因为用于访问之的 Win32 API 拒绝设置超长变量。至于“什么都不做”的时候可以读到超长环境变量,单纯是 Windows 从注册表读取的时候、通过 GetEnvironmentVariable 返回的时候,忘记验证了。
考虑不变式
LPCTSTR name = T("verylong");
TCHAR value[40001];
DWORD size = 40001;
DWORD actualSize = GetEnvironmentVariable(name, value, size);
BOOL couldSet = SetEnvironmentVariable(name, value);
自然期待单线程环境下 actualSize > 0 && actualSize < size (这表明获取名字是 verylong 的环境变量成功了)蕴涵着 couldSet (这表示可以做无意义且无害的赋值),但这个不变式因为 Windows 的实现很不小心所以不一定成立。
然后,在做了上面的实验之后,我会认为日常使用的时候应该避免变量内容超过 4095 个字符,因为 Windows 最常见的 shell 即 explorer.exe 不支持超长的变量。