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

Windows Via C/C++ Part Ⅰ Chapter4: 进程—第一个Windows程序(4)

2009-08-31 21:44 459 查看
进程的亲缘性

  通常,进程中的线程可以在主机的任何CPU中执行,在有些情况下,你可能想让线程只运行在指定的CPU子集中,这叫做“处理器亲缘性”,我们将在第七章“线程调度、优先级和亲缘性”中讨论这个话题。

进程的错误模式

  每个进程都关联着一组标志位,用来告诉系统进程如何响应严重错误,比如磁盘介质错误、未处理的异常、文件未找到和数据偏差等。进程通过SetErrorMode函数设置该行为:
UINT SetErrorMode(UINT fuErrorMode);
参数fuErrorMode的取值可以是表4-3中任何标志位的组合.



  子进程默认继承父进程的错误模式。比如进程A打开了SEM_NOGPFAULTERRORBOX标志并创建了进程B,那么进程B的错误模式中也会有SEM_NOGPFAULTERRORBOX。但子进程B并不知道自己继承了该错误模式标志,它可能没有准备处理GP错误的代码。如果B中的任一线程运行时发生了GP错误,进程B会因此终止而用户得不到任何消息。父进程可以在CreateProcess函数中指定CREATE_DEFAULT_ERROR_MODE标志,以阻止子进程继承其错误模式。

进程的当前驱动器和目录

  当Windows函数需要文件/目录路径为其参数,调用者又没有提供完整路径时,函数会在当前驱动器的当前目录中查找该路径。比如,线程调用CreateFile打开文件且没有指定完整文件路径时,系统会在当前驱动器的当前目录中查找该文件。

  系统在其内部保存进程的当前驱动器和当前目录并跟踪其变化。由于当前驱动器和当前目录是以进程为单位保存的,因此如果进程中的任何线程改变了当前驱动器或目录时,进程中的所有线程都会受到影响。线程可以通过下面的函数获取并设置其当前驱动器/目录:
DWORD GetCurrentDirectory(DWORD cchCurDir, PTSTR pszCurDir);
BOOL SetCurrentDirectory(PCTSTR pszCurDir);
如果你提供的缓冲区pszCurDir不够大,GetCurrentDirectory会返回存储结果所需的字符数目(包括结束符0),且不对pszCurDir做任何更改。调用成功时,GetCurrentDirectory函数返回获得的字符串长度,不包括结束符0。在WinDef.h中定义的MAX_PATH宏是目录/文件路径的最大长度(260),因此将MAX_PATH作为GetCurrentDirectory的cchCurDir参数总是安全的。

进程的当前目录

  系统会跟踪进程的当前驱动器和目录,但并不会跟踪每个驱动器的当前目录。然而有些操作系统支持对多个驱动器的当前目录的处理,这是通过进程环境变量实现的。比如进程C盘的当前目录是/Utility/Bin,D盘的当前目录是/Program Files,那么其环境块中会包含下面的内容:
=C:=C:/Utility/Bin
=D:=D:/Program Files

  如果你在调用函数时传递的参数中包含驱动器修饰名(如C:、D:),而该驱动器并不是进程的当前驱动器,系统就会在进程环境块中查找和指定的驱动器名关联的变量。如果变量存在,系统使用变量值作为当前目录,如果不存在,系统将使用指定驱动器的根目录做为当前目录。比如进程当前目录是C:/Utility/Bin,当你调用CreateFile打开文件D:ReadMe.txt时,系统会在环境变量中查找变量=D: ,由于=D: 已经存在且其值为D:/Program Files,系统就打开文件D:/Program Files/ReadMe.txt。假如=D:不存在,系统将在D盘根目录下打开文件ReadMe.txt。Windows文件处理函数不会增加或改变驱动器环境变量,它们只是读取其值。

  除了使用SetCurrentDirectory改变进程当前目录,你还可以调用CRT函数_chdir,_chdir在其内部调用了SetCurrentDirectory,同时它也调用SetEnvironmentVariable添加或者修改了进程的驱动器环境变量,这样不同驱动器的当前目录可以被保存下来,而SetCurrentDirectory则仅改变当前驱动器和当前目录,不会对进程环境变量做任何修改。

  假如父进程在创建子进程时指定了子进程的环境变量块(通过CreateProcess的pvEnvironment参数),那么子进程就不会继承父进程自身的环境变量,包括当前目录变量,此时子进程中每个驱动器的当前目录默认为每个驱动器的根目录。此时假如你想让子进程继承父进程的当前目录,父进程必须把这些变量添加到为子进程准备的环境变量块,然后再创建子进程。父进程可以调用函数GetFullPathName获得自己的当前目录:
DWORD GetFullPathName(PCTSTR pszFile, DWORD cchPath, PTSTR pszPath, PTSTR *ppszFilePart);


比如为了获取C驱动器的当前目录,你可以做如下调用:
TCHAR szCurDir[MAX_PATH];
DWORD cchLength = GetFullPathName(TEXT("C:"), MAX_PATH, szCurDir, NULL);
在取得了这些变量之后,按习惯,你应该将它们放在环境变量块开头。

  下面的代码对本节的概念做了一些说明:

#include <windows.h>
#include <tchar.h>
#include <crtdbg.h>
#include <strsafe.h>
#include <stdlib.h>

void DumpEnvStrings();

int _tmain()
{
/* 用SetCurrentDirectory改变当前驱动器和目录,然后打印环境块,
可以看到环境块没有发生任何变化 */
_tprintf(TEXT("Via SetCurrentDirectory:/r/n"));
_tprintf(TEXT("-------------------------------------------/r/n"));
DumpEnvStrings();

_tprintf(TEXT("-------------------------------------------/r/n"));
SetCurrentDirectory(TEXT("c:/windows"));
DumpEnvStrings();

_tprintf(TEXT("-------------------------------------------/r/n"));
SetCurrentDirectory(TEXT("d:/temp"));
DumpEnvStrings();

_tprintf(TEXT("-------------------------------------------/r/n"));
SetCurrentDirectory(TEXT("e:/game/cs.1.6"));
DumpEnvStrings();

/* 用_tchdir改变当前驱动器和目录,然后打印环境块,注意环境变量
中各个驱动器的当前目录的变化 */
_tprintf(TEXT("Via _tchdir:/r/n"));
_tprintf(TEXT("-------------------------------------------/r/n"));
DumpEnvStrings();

_tprintf(TEXT("-------------------------------------------/r/n"));
_tchdir(L"c:/windows");
DumpEnvStrings();

_tprintf(TEXT("-------------------------------------------/r/n"));
_tchdir(L"d:/temp");
DumpEnvStrings();

_tprintf(TEXT("-------------------------------------------/r/n"));
_tchdir(L"e:/game/cs.1.6");
DumpEnvStrings();

/* 获得d驱动器的当前目录,应该是d:/temp */
TCHAR szCurDir[MAX_PATH];
GetFullPathName(TEXT("d:"),MAX_PATH,szCurDir, NULL);
_tprintf(L"%s/r/n",szCurDir);

/* 注意文件名格式E:Readme.txt,这种命名方式会让函数在变量块中查找变量=E:,
以获得E驱动器的当前目录,然后在该目录下创建Readme.txt文件,找不到=E:变
量时,函数会在E驱动器根目录下创建Readme.txt文件 */
HANDLE hf = CreateFile(TEXT("E:Readme.txt"),GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);

return 0;
}

/** DumpEnvStrings打印出进程环境块中以=号开头的所有环境变量 */
void DumpEnvStrings()
{
PTSTR pEnvBlock = GetEnvironmentStrings();
PTSTR pCurrent = pEnvBlock;

while(*pCurrent != TEXT('/0')) {
if(*pCurrent == TEXT('='))
_tprintf(L"%s/r/n",pCurrent);
while(*pCurrent != TEXT('/0'))
++ pCurrent;
++ pCurrent;
}
}


系统版本

  应用程序有时需要Windows系统的版本信息。比如程序可能要用到处理Windows事务文件系统的函数如CreateFileTransacted,但只有Vista及后续版本的操作系统才支持事务文件操作。

  Windows API提供了GetVersion函数:
DWORD GetVersion();
GetVersion起初是为16位Windows设计的,其返回值的高字表示MS-DOS版本号,低字表示Windows版本号,每个字的高字节表示主版本号,低字节表示次版本号。不幸的是,实现该函数的程序员犯了一个小错误——将Windows版本号的主版本号写在了返回值的低字节,而把次版本号写在了高字节。因为发现该bug时GetVersion已被广泛使用,微软被迫保留了这个小bug并修改了文档以反应这个问题。

  GetVersion的返回值可能会使开发人员混淆,因此微软增加了函数GetVersionEx:
BOOL GetVersionEx(POSVERSIONINFOEX pVersionInformation);
GetVersionEx接收一个指向OSVERSIONINFOEX结构的指针为其参数,OSVERSIONINFOEX结构定义如下:
typedef struct {
DWORD dwOSVersionInfoSize;
DWORD dwMajorVersion;
DWORD dwMinorVersion;
DWORD dwBuildNumber;
DWORD dwPlatformId;
TCHAR szCSDVersion[128];
WORD  wServicePackMajor;
WORD  wServicePackMinor;
WORD  wSuiteMask;
BYTE  wProductType;
BYTE  wReserved;
} OSVERSIONINFOEX, *POSVERSIONINFOEX;
OSVERSIONINFOEX结构在Windows 2000及后续版本的系统中定义,之前版本的Windows使用较旧的OSVERSIONINFO结构。表4-4是OSVERSIONINFOEX结构各成员的说明:



在MSDN的"Getting the System Version"(http://msdn2.microsoft.com/en-gb/library/ms724429.aspx)小节,你可以看到使用OSVERSIONINFOEX结构获取系统信息的详细例子。

[原文在此讲述了VerifyVersionInfo的用法,该函数主要用于比较系统信息,类似于GetVersionEx,此处不译,有兴趣的朋友可以自己参阅MSDN]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: