您的位置:首页 > 编程语言 > C语言/C++

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获得环境块,解析后打印出其中内容:
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,函数将删除该环境变量。
在操纵你自己的进程环境块时,你应该总是使用上面提到的函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: