Windows Via C/C++ Part Ⅰ Chapter4: 进程—第一个Windows程序(3)
2009-08-30 13:17
483 查看
进程的环境变量
每一个进程都有与其关联的环境块。进程环境块是在进程地址空间中分配的内存区域,其中含有类似下面格式的一组字符串:
=::=::/ ...
VarName1=VarValue1/0
VarName2=VarValue2/0
VarName3=VarValue3/0 ...
VarNameX=VarValueX/0
/0
每个字符串的第一部分是环境变量名,=号后面是相应的值。注意以=号开头的字符串并不是环境变量,我们在“进程的当前目录”一节会谈到这个问题。
前面已经介绍过访问进程环境块的两种方式,每一种方式使用不同的解析方法,产生的结果也不一样。第一种访问完整进程块的方法是调用GetEnviromentStrings函数。前面的章节已介绍过它的调用格式,下面的代码利用GetEnvironmentStrings获得环境块,解析后打印出其中内容:
访问环境变量的第二种方法只适用于CUI程序。CUI程序的入口点_tmain提供了参数env,它是一个指针数组,其中的每个元素指向一个以“name=value”格式存储的字符串,数组的最后一个指针值为NULL,作为边界哨兵。以下代码打印了env中的所有的元素:
环境变量定义包含空格,比如 XYZ =Windows 和 XYZ=Windows 就是两个不同的环境变量,前者的变量名中包含空格,为“XYZ ”,后者的是“XYZ”。变量值中的空格也是有效的,比如XYZ= Windows 和 ABC=Windows,两者的值并不相同,XYZ变量的值是“ Windows”包含空格。
用户登录Windows时,系统将创建一个shell进程,并将一组环境变量与该进程相关联。系统访问注册表中的两个键获得环境变量,HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Session Manager/Environment包含了系统级环境变量,而HKEY_CURRENT_USER/Environment则包含了当前用户的环境变量。应用程序可以使用注册表编辑函数修改这些注册表入口的值。不过如果要让改变对所有程序生效,用户需要注销并重新登录系统。有些程序——比如资源管理器、任务管理器和控制面板——在主窗口收到WM_SETTINGCHANGE消息时会用新的注册表入口值更新其环境块。这样你就可以使用SendMessage函数通知对环境变量的更改感兴趣的程序,在收到WM_SETTINGCHANGE消息时同步其环境块。
通常子进程会继承父进程的进程环境块。但是父进程可以通过CreateProcess函数控制该继承行为,在“CreateProcess函数”一节中我们会谈到。子进程在创建时会拥有父进程环境块的完整拷贝,而不是共享父进程的环境块。这意味着子进程可以随意更改自己的环境块,而不会影响到父进程。
你可以使用GetEnvironmentVariable来检查某个环境变量是否存在并获得其值:
在Windows用户和系统环境变量中,你可以发现许多类似 %USERPROFILE%/Documents 这样的环境变量值,其中的%之间的部分是可替换字符串。比如 %SYSTEMROOT% 在我的机器上表示 c:/windows 目录。由于这种替换比较常见,Windows提供了ExpandEnvironmentStrings函数:
最后你可以使用SetEnvironmentVariable函数添加、删除、修改环境变量:
在操纵你自己的进程环境块时,你应该总是使用上面提到的函数。
每一个进程都有与其关联的环境块。进程环境块是在进程地址空间中分配的内存区域,其中含有类似下面格式的一组字符串:
=::=::/ ...
VarName1=VarValue1/0
VarName2=VarValue2/0
VarName3=VarValue3/0 ...
VarNameX=VarValueX/0
/0
每个字符串的第一部分是环境变量名,=号后面是相应的值。注意以=号开头的字符串并不是环境变量,我们在“进程的当前目录”一节会谈到这个问题。
前面已经介绍过访问进程环境块的两种方式,每一种方式使用不同的解析方法,产生的结果也不一样。第一种访问完整进程块的方法是调用GetEnviromentStrings函数。前面的章节已介绍过它的调用格式,下面的代码利用GetEnvironmentStrings获得环境块,解析后打印出其中内容:
void DumpEnvStrings() { PTSTR pEnvBlock = GetEnvironmentStrings(); // Parse the block with the following format: // =::=::/ // =... // var=value/0 // ... // var=value/0/0 // Note that some other strings might begin with '='. // Here is an example when the application is started from a network share. // [0] =::=::/ // [1] =C:=C:/Windows/System32 // [2] =ExitCode=00000000 // TCHAR szName[MAX_PATH]; TCHAR szValue[MAX_PATH]; PTSTR pszCurrent = pEnvBlock; HRESULT hr = S_OK; PCTSTR pszPos = NULL; int current = 0; while (pszCurrent != NULL) { // Skip the meaningless strings like: // "=::=::/" if (*pszCurrent != TEXT('=')) { // Look for '=' separator. pszPos = _tcschr(pszCurrent, TEXT('=')); // Point now to the first character of the value. pszPos++; // Copy the variable name. size_t cbNameLength = // Without the' =' (size_t)pszPos - (size_t)pszCurrent - sizeof(TCHAR); hr = StringCbCopyN(szName, MAX_PATH, pszCurrent, cbNameLength); if (FAILED(hr)) { break; } // Copy the variable value with the last NULL character // and allow truncation because this is for UI only. hr = StringCchCopyN(szValue, MAX_PATH, pszPos, _tcslen(pszPos)+1); if (SUCCEEDED(hr)) { _tprintf(TEXT("[%u] %s=%s/r/n"), current, szName, szValue); } else // something wrong happened; check for truncation. if (hr == STRSAFE_E_INSUFFICIENT_BUFFER) { _tprintf(TEXT("[%u] %s=%s.../r/n"), current, szName, szValue); } else { // This should never occur. _tprintf( TEXT("[%u] %s=???/r/n"), current, szName ); break; } } else { _tprintf(TEXT("[%u] %s/r/n"), current, pszCurrent); } // Next variable please. current++; // Move to the end of the string. while (*pszCurrent != TEXT('/0')) pszCurrent++; pszCurrent++; // Check if it was not the last string. if (*pszCurrent == TEXT('/0')) break; }; // Don't forget to free the memory. FreeEnvironmentStrings(pEnvBlock); }代码的功能在注释中已经比较清楚了。要注意用GetEnvironmentStrings获得的环境区不再使用时应该用FreeEnvironmentStrings释放。
访问环境变量的第二种方法只适用于CUI程序。CUI程序的入口点_tmain提供了参数env,它是一个指针数组,其中的每个元素指向一个以“name=value”格式存储的字符串,数组的最后一个指针值为NULL,作为边界哨兵。以下代码打印了env中的所有的元素:
void DumpEnvVariables(PTSTR pEnvBlock[]) { int current = 0; PTSTR* pElement = (PTSTR*)pEnvBlock; PTSTR pCurrent = NULL; while (pElement != NULL) { pCurrent = (PTSTR)(*pElement); if (pCurrent == NULL) { // No more environment variable. pElement = NULL; } else { _tprintf(TEXT("[%u] %s/r/n"), current, pCurrent); current++; pElement++; } } }环境块中所有以=号开头的字符串在调用入口点函数_tmain之前,已被CRT启动函数从中删除,因此_tmain接收的env数组中仅包含形如“name=value”的环境变量。
环境变量定义包含空格,比如 XYZ =Windows 和 XYZ=Windows 就是两个不同的环境变量,前者的变量名中包含空格,为“XYZ ”,后者的是“XYZ”。变量值中的空格也是有效的,比如XYZ= Windows 和 ABC=Windows,两者的值并不相同,XYZ变量的值是“ Windows”包含空格。
用户登录Windows时,系统将创建一个shell进程,并将一组环境变量与该进程相关联。系统访问注册表中的两个键获得环境变量,HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Session Manager/Environment包含了系统级环境变量,而HKEY_CURRENT_USER/Environment则包含了当前用户的环境变量。应用程序可以使用注册表编辑函数修改这些注册表入口的值。不过如果要让改变对所有程序生效,用户需要注销并重新登录系统。有些程序——比如资源管理器、任务管理器和控制面板——在主窗口收到WM_SETTINGCHANGE消息时会用新的注册表入口值更新其环境块。这样你就可以使用SendMessage函数通知对环境变量的更改感兴趣的程序,在收到WM_SETTINGCHANGE消息时同步其环境块。
通常子进程会继承父进程的进程环境块。但是父进程可以通过CreateProcess函数控制该继承行为,在“CreateProcess函数”一节中我们会谈到。子进程在创建时会拥有父进程环境块的完整拷贝,而不是共享父进程的环境块。这意味着子进程可以随意更改自己的环境块,而不会影响到父进程。
你可以使用GetEnvironmentVariable来检查某个环境变量是否存在并获得其值:
DWORD GetEnvironmentVariable( PCTSTR pszName, PTSTR pszValue, DWORD cchValue);pszName参数指向要检测/取值的环境变量名,pszValue是输出参数,指向接收变量值的缓冲区,cchValue表明确pszValue缓冲区的大小。函数返回复制到pszValue缓冲区的字符个数,当pszName指示的环境变量不存在时,函数返回0。当你向cchValue传递0时,函数返回pszValue接收变量值所需的缓冲区字符个数(包括结束符0)。以下代码展示了如何安全的使用该函数:
void PrintEnvironmentVariable(PCTSTR pszVariableName) { PTSTR pszValue = NULL; // Get the size of the buffer that is required to store the value DWORD dwResult = GetEnvironmentVariable(pszVariableName, pszValue, 0); if (dwResult != 0) { // Allocate the buffer to store the environment variable value DWORD size = dwResult * sizeof(TCHAR); pszValue = (PTSTR)malloc(size); GetEnvironmentVariable(pszVariableName, pszValue, size); _tprintf(TEXT("%s=%s/n"), pszVariableName, pszValue); free(pszValue); } else { _tprintf(TEXT("'%s'=<unknown value>/n"), pszVariableName); } }
在Windows用户和系统环境变量中,你可以发现许多类似 %USERPROFILE%/Documents 这样的环境变量值,其中的%之间的部分是可替换字符串。比如 %SYSTEMROOT% 在我的机器上表示 c:/windows 目录。由于这种替换比较常见,Windows提供了ExpandEnvironmentStrings函数:
DWORD ExpandEnvironmentStrings(PTCSTR pszSrc, PTSTR pszDst, DWORD chSize);pszSrc指向包含“%[可替换字符串]%”的字符串,pszDst用于接收转换后的结果,chSize参数是pszDst缓冲区的大小。函数返回pszDst接收转换结果所需的大小(以字符计),如果chSize小于该值,函数将%之间的字符串置空。你可以像下面这样调用该函数:
DWORD chValue = ExpandEnvironmentStrings(TEXT("PATH='%PATH%'"), NULL, 0); PTSTR pszBuffer = new TCHAR[chValue]; chValue = ExpandEnvironmentStrings(TEXT("PATH='%PATH%'"), pszBuffer, chValue); _tprintf(TEXT("%s/r/n"), pszBuffer); delete[] pszBuffer;
最后你可以使用SetEnvironmentVariable函数添加、删除、修改环境变量:
BOOL SetEnvironmentVariable( PCTSTR pszName, PCTSTR pszValue);函数将名为pszName的环境变量的值设置为pszValue的值。如果指定的环境变量已存在,函数将修改其值。如果不存在,函数将添加新的环境变量,如果pszValue为NULL,函数将删除该环境变量。
在操纵你自己的进程环境块时,你应该总是使用上面提到的函数。
相关文章推荐
- Windows Via C/C++ Part Ⅰ Chapter4: 进程—第一个Windows程序(4)
- Windows Via C/C++ Part Ⅰ Chapter4: 进程—第一个Windows程序(2)
- Windows Via C/C++ Part Ⅰ Chapter4: 进程—第一个Windows程序(1)
- Windows Via C/C++ Part Ⅰ Chapter4: 进程(1)_概述
- 【Caffe的C++接口使用说明(一)】caffe_windows下的第一个测试程序学习教程
- Windows via C/C++ Part 1: Required Reading - Chapter1 错误处理
- C/C++_log2000_windows编程之第一个控制台程序项目part2
- Windows Via C/C++ Part Ⅰ Chapter3: 内核对象(3)
- Windows via C/C++:终止进程
- C++之第一个windows程序,win32 helloworld
- Windows via C/C++ —— 进程(一)读书笔记
- Windows Via C/C++ Part Ⅰ Chapter 2: 字符和字符串(2)
- Windows编程C++之第一个Windows程序
- 《Windows via C/C++》学习笔记 —— 进程
- Windows Via C/C++ Part Ⅰ Chapter3: 内核对象(2)
- Windows Via C/C++ Part Ⅰ Chapter 2: 字符和字符串(3)
- Windows Via C/C++ Part Ⅰ Chapter3: 内核对象(1)
- Windows Via C/C++ Part Ⅰ Chapter 2: 字符和字符串(1)
- Windows Via C/C++ Part Ⅰ Chapter 2: 字符和字符串(4)
- 【转】Gvim配置(Windows and Linux)for C++|gvim编译运行c/c++程序