[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

[系统相关] [讨论]环境变量的存储(2011-05-18更新)

05-18修订

  • 修订Windows环境空间的分类以及优先级
  • 增加无需注销更新系统环境变量的方案


05-06修订

  • 增加Windows环境变量的注册表存储位置
  • 修改explorer.exe下修改环境变量的说明


4-30修订

  • 增加Windows的环境变量排序、动态变量、和setlocal/endlocal
  • 增加msdos的set命令、autoexec.bat、setx.exe工具的说明
  • 修改windows下环境空间和变量读写的说明


因为看了置顶主题“批处理变量表机制的猜测及测试”
http://www.bathome.net/thread-12030-1-1.html
有感于中间讨论的东西太多太杂
很多常识性的概念和论点被反复讨论
大段的测试代码看的让人头晕
又充斥了一堆似是而非甚至是谬误的言论
所以将变量存储的一些细节另发主题
这里是纯理论探讨
代码测试请另开主题

一、MS-DOS下的环境变量
MS-DOS系统太久远了
关于环境变量的处理这里不做过多的讨论
只大概明确两点
1.环境变量空间分为两类:全局和局部
全局环境由系统核心COMMAND.COM独立创建和维护
局部环境由COMMAND载入exe等应用程序时创建
局部环境创建时会复制全局环境的值
环境空间地址写入程序的PSP
但应用程序结束后不会写会全局环境
大多数操作环境的函数都是针对局部环境的
要写入全局环境需要通过特殊手段得到全局环境的起始地址
但是set命令是针对全局环境进行操作的
因为它是command.com的内部命令
它必然有command.com解释执行
所以自然会索引到command.com的环境空间
也就是全局环境空间
通过C盘根目录的config.sys和autoexec.bat文件可以配置MS-DOS的环境变量

2.MSDOS环境变量是按字节存储的
是按ASCII表顺序存储
变量名会统一转换为大写处理
所以不存在大小写的比较问题
COMSPEC例外,因为它在系统启动时指引IO.SYS寻找COMMAND.COM
其形式是“ 【变量名】=【变量值】【字节0】”
读取变量的方式是顺序遍历
之所以没有用二分搜索遍历
是因为那是的变量空间相对较小
变量的使用频率也相对较低
相对于与变量的索引性能而言
顺序遍历简单而够用
写入变量的方式是顺序遍历后插入
也就是说新变量后的变量会顺序后移

二、Windows下的环境变量
到了Windows系统下发生了很多变化

首先,环境空间变成了三级:
1)系统环境空间,存储在注册表 HKLM\System\CurrentControlSet\Control\Session Manager\Environment\
2)用户环境空间,存储在注册表 HKCU\Environment\  和  HKCU\Volatile Environment
3)启动环境空间,存储在文件 系统盘:\AUTOEXEC.BAT

为了兼容性考虑
位于系统盘根目录的autoexec.bat
仍可以预定义Windows的环境变量
为了安全性考虑
系统只解释autoexec.bat中的环境变量设置语句
如set、path、append等
其它语句不予理会

系统启动时应用程序根据自己的需要读取环境变量
然后写入应用程序自己的环境空间
如果某个环境变量在多个环境空间被定义
将按照“系统、启动、用户”的顺序依次读取并设置
最后被设置的值为环境变量的当前值

path变量是一个特例
它是由"系统、用户、启动"三级空间的对应值组合而来的
也就是说当前path=系统path+用户path+启动path
同样的例子还有LibPath 和 Os2LibPath

应用程序的环境空间有继承特性
如果A进程启动了B进程
那么B进程作为A进程的子进程
将缺省集成A进程的环境空间

而MS-DOS的全局环境空间
类似于现在Windows下explorer.exe的环境空间
因为explorer.exe是Windows默认的外壳
很多情况下用户所启动的程序
包括从开始运行输入cmd、点击“命令提示符”或者批处理文件所启动的cmd.exe
都是作为explorer.exe的子进程运行
所以会继承它的环境空间

两类环境变量通常在“控制面板”的“系统属性”中更改
这不仅了修改了注册表中的相对应的键值
也同时修改了explorer.exe自己的环境空间
所以不用重启电脑马上可以在cmd.exe看到这个变化

也可以直接修改注册表中对应子键的键值
但是需要注销当前用户或重启计算机
由userinit.exe进程重新读取到新的变量状态
从而继承到explorer.exe的环境空间中
在新启动的类似cmd.exe的子进程中才能观察到这种变化

若要无需注销而更新环境空间
可以向所有窗口发送一个 WM_SETTINGCHANGE 广播消息
相关的应用程序(资源管理器、任务管理器、控制面板等)会执行更新
代码示例
  1. SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
  2.     (LPARAM) "Environment", SMTO_ABORTIFHUNG,
  3.     5000, &dwReturnValue);
复制代码
另外,在微软发行的Windows XP Service Pack 2 Support Tools(并非SP2)中
附带了一个命令行工具setx.exe
它可以在命令行中直接修改用户变量或者系统变量

其次,因为Windows系统自2000版本开始内部已全面采用Unicode编码
所以Windows的环境变量也是以Unicode形式存储
也就是说无论英文、汉字还是日文都会占用两个字节的空间
这也就解释了用set获取变量的长度或者子串时都是以字符计数
而非以ANSI存储(对于ASCII是单字节,对于GBK是双字节)的字节计数

环境变量的排序规则也从ASCII更新到了Unicode编码排序
只不过因为Windows支持了小写的变量名形式
所以它的排序比较是忽略大小写的
包括全局英文的大小写也被忽略

再次,对于Windows下的COMMAND.COM曾经有过讨论
它仅仅是虚拟机(ntvdm)封装下的cmd.exe的入口程序
当我们启动command.com时
实际上是启动了ntvdm.exe进程
这个ntvdm不仅仅提供了command命令到cmd.exe命令的虚拟通道
也对16位程序所需的内存环境做了虚拟
包括内存中的环境变量
可以通过创建PIF文件(16位DOS程序的快捷方式)定制内存环境(包括变量环境)
也可以修改%winir%\system32下的CONFIG.NT和AUTOEXEC.NT配置默认的虚拟内存环境

NTVDM虚拟环境最主要的变化就是
把双字节的Unicode映射为了单字节的ASCII编码和双字节的本地化编码(GB)
而debug.exe因为程序本身是为16位环境设计
必须运行在NT虚拟机环境下
启动debug.exe则同时会启动ntvdm.exe
所以debug操作的是ntvdm虚拟出的内存环境
所以它所看到的是单字节的环境变量存储

另外,cmd下多了很多特殊的环境变量
如果cmd的命令扩展被启用(默认是启用状态)
有几个动态环境变量可以被引用
每次变量数值被扩展时
这些变量数值都会被动态计算
它们不会出现在 SET 显示的变量列表中
也不存在于cmd的环境空间中
%CD% - 扩展到当前目录字符串。
%__CD__% - 扩展到当前目录字符串。末尾总会附带分隔路径的斜线。
%DATE% - 用跟 DATE 命令同样的格式扩展到当前日期。
%TIME% - 用跟 TIME 命令同样的格式扩展到当前时间。
%RANDOM% - 扩展到 0 和 32767 之间的任意十进制数字。
%ERRORLEVEL% - 扩展到当前 ERRORLEVEL 数值。
%CMDEXTVERSION% - 扩展到当前命令处理器扩展名版本号。
(NT4下是1;2000,XP下是2;Vista,Win7未确认)
%CMDCMDLINE% - 扩展到调用命令处理器的原始命令行。

还有一些动态变量更为特殊
它们占用了环境空间但是却不会出现在set命令的列表中
不过可以用一些不常见的用法看到它的存在 set,
即set后面直接跟一个半角逗号(也可以是分号或者 set "")
在命令输出的最上方会多出类似下面的变量
=::=::\         标识未访问盘符的当前路径
=C:=C:\Documents and Settings\Administrator   标识已访问盘符的当前路径
=ExitCode=00000001      以32位的16进制数标识Exit /b所指定的错误返回码
它使十进制到十六进制数据转换更加快捷
=ExitCodeASCII=00000001      以ASCII表中对应的字符标识Exit /b所指定的错误返回码
指定的返回码必须大于等于32(也就是空格的ASCII码值)它才会被设置
它使ASCII码值转字符更加方便

最后说说setlocal/endlocal
它会将cmd.exe的当前环境空间另选新的内存空间备份一份
直到endlocal把这个备份空间恢复到当前空间并回收内存
嵌套使用setlocal时会将环境空间多次备份
而后endlocal时会从新到旧恢复空间并收回内存
3

评分人数

    • 523066680: 细致入微PB + 30 技术 + 10
    • CrLf: 当时没看懂,现在才明白此贴的价值技术 + 1
    • applba: 膜拜技术 + 1
天的白色影子

基本看不懂

TOP

回复 46# CrLf
我只用过/min

TOP

应该是task后start

TOP

这个你有测试成功吗?
天的白色影子

TOP

50# qzwqzw
会不会是选择的键无效,如修改"我的文档"下述位置是无效的
  1. HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
复制代码

TOP

49# temp
试了两台PC
均为XP SP2
表现均为注册表更新
而实际cmd未更新
倒是批处理中HDD变量
因为没有setlocal
结果被explorer继承
成为伪全局变量
附件: 您需要登录才可以下载或查看附件。没有帐号?注册
天的白色影子

TOP

41# temp
测试了一下
因为没有现成的inf文件
直接regedit修改HKEY_CURRENT_USER\Environment下的已有变量
然后杀进程、刷新、启进程
结果无效!

另外印象中有直接刷新当前Explorer.exe环境空间的命令行
似 ...
qzwqzw 发表于 2011-5-17 21:07

试试这样行不行
  1. @echo off
  2. title=        
  3. :start
  4. set /p NetConf=请输入你选择的操作(1修改;2还原):
  5. if /i "%NetConf%" equ "1" (goto 1) else if /i "%NetConf%" equ "2" (goto 2) else (goto :eof)
  6. :1
  7. echo.
  8. Set HDD=
  9. set /p HDD=完整的路经及文件名[如 d:\temp ]:
  10. if not exist "%HDD%" (md "%HDD%")
  11. (echo [Version]
  12. echo Signature="$CHICAGO$"
  13. echo.
  14. echo [DefaultInstall]
  15. echo AddReg=AddReg
  16. echo [AddReg]
  17. echo hkcu,"Environment","TEMP",0x20000,"%HDD%"
  18. echo hkcu,"Environment","TMP",0x20000,"%HDD%"
  19. echo [Strings])>>.\Modift.inf
  20. rundll32 setupapi,InstallHinfSection DefaultInstall 132 .\Modift.inf
  21. del /f /s /q .\Modift.inf
  22. taskkill.exe /im explorer.exe /f
  23. RunDll32 USER32,UpdatePerUserSystemParameters
  24. start explorer.exe
  25. exit
  26. :2
  27. (echo [Version]
  28. echo Signature="$CHICAGO$"
  29. echo.
  30. echo [DefaultInstall]
  31. echo AddReg=AddReg
  32. echo [AddReg]
  33. echo hkcu,"Environment","TEMP",0x20000,"%%USERPROFILE%%\Local Settings\Temp"
  34. echo hkcu,"Environment","TMP",0x20000,"%%USERPROFILE%%\Local Settings\Temp"
  35. echo [Strings])>Modift.inf
  36. rundll32 setupapi,InstallHinfSection DefaultInstall 132 .\Modift.inf
  37. del /f /s /q .\Modift.inf
  38. taskkill.exe /im explorer.exe /f
  39. RunDll32 USER32,UpdatePerUserSystemParameters
  40. start explorer.exe
  41. exit
复制代码

TOP

“这也就解释了用set获取变量的长度或者子串时都是以字符技术”

最好两个字错了
1

评分人数

TOP

41# temp
测试了一下
因为没有现成的inf文件
直接regedit修改HKEY_CURRENT_USER\Environment下的已有变量
然后杀进程、刷新、启进程
结果无效!

另外印象中有直接刷新当前Explorer.exe环境空间的命令行
似 ...
qzwqzw 发表于 2011-5-17 21:07

把的注册表文件直接贴上来吧,我把现成测试好的inf格式文件贴上来

TOP

45# caruko


我都没怎么注意过start的开关,平时用得太少,看来以前忽略了太多很多值得发掘的东西

TOP

本帖最后由 caruko 于 2011-5-17 22:34 编辑

一般来说,子进程都会继承父进程的环境变量。
start /I 命令也可以放弃继承,使用新环境,也就是初始环境。

有一次使用bat来做open vpn密码验证程序,发现OPENVPN的登陆密码和用户名是以环境变量的形式存在的,这样方便调用外部程序传递一些数据。
当时还担心过它的安全,后来查了一些资料才了解。

TOP

41# temp
测试了一下
因为没有现成的inf文件
直接regedit修改HKEY_CURRENT_USER\Environment下的已有变量
然后杀进程、刷新、启进程
结果无效!

另外印象中有直接刷新当前Explorer.exe环境空间的命令行
似乎也是用的rundll32
可惜找不到了
天的白色影子

TOP

此文对cmd 环境变量分析总结的比较透彻,谢谢LZ

顺便问下 set,这个是怎么发现的呢? 以前从来没见过


hanyeguxing 斑竹
对NT内核了如指掌啊,佩服

TOP

39# ▄︻┻═┳一


不试试41楼的方法?
我帮忙写的代码不需要付钱。如果一定要给,请在微信群或QQ群发给大家吧。
【微信公众号、微信群、QQ群】http://bbs.bathome.net/thread-3473-1-1.html
【支持批处理之家,加入VIP会员!】http://bbs.bathome.net/thread-67716-1-1.html

TOP

返回列表