Windows核心编程-第五版(中文).pdf
《Windows核心编程-第五版(中文).pdf》由会员分享,可在线阅读,更多相关《Windows核心编程-第五版(中文).pdf(150页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、继往开来创新高,推陈出新品佳酿继往开来创新高,推陈出新品佳酿 1993 年,本书第 1 版 Advanced NT 出版的时候,我和三个朋友一起成立了一个“四喜工作室”。由于四个人只有一台计算机,所以我们几个每天一睁眼,第一件事情便是抢占计算机,这台 386 配置简单,根本无法与现在的计算机相提并论,而且当时也没有网络,所以计算机的用途非常有限,主要也就是文字处理,玩游戏,编简单程序等,但它带给我们的乐趣至今难以忘怀。受限于当时的环境,数据和游戏的交换也基本上在圈内好友之间进行,就像搞地下活动一样约好时间地点碰头。幸运的是,由此结交了一大批计算机爱好者,后来他们大多成为 IT 届的领军人物。其
2、时,从大环境看,我国网络也开始悄然起步。1993 年年初,中国科学院高能物理研究所接入斯坦福大学线性加速器中心的 64K 专线开通,国内科学家开始在国内使用电子邮件。随后几个月的时间,金桥工程和域名体系的确立和部署,三大院校网的连接,最终将我国带入信息高速公路,推动我国 IT 业的迅猛发展。由此而来的便是计算机类图书和报纸期刊的炙手可热,电脑报等 IT 媒体相继崛起,计算机图书更是出现供不应求的现象,在当时,即便是国外引进翻译出版的图书,也能轻松突破几万册的销量,计算机图书的发展达到全盛时期。在这个时期,国内开发人员先后成为 Jeffrey 和 McConell 等大师的拥趸。因为在 IT 界
3、,虽然资深程序员不胜枚举,但同时又是深受程序员喜爱的技术图书作家的乏善可陈。而像他们那样,曾经写过多部书,部部都引人入胜,令人醍醐灌顶,就更是凤毛麟角。他们是 Windows编程世界中的中流砥柱,也是 Windows 技术当之无愧的布道者。曾有不少读者放言,只要是 Jeffrey 的书,他们必定会花时间研读,并加以收藏。这一点都不夸张,我们同时代的很多人都是在这批书的滋润下成长起来的。他们熟读了 Advanced NT 之后,又如痴如狂地捧起了 Advanced Windows 和 Programming Application for Microsoft Windows 等续作。他们是Jef
4、frey 的粉丝,同时也是微软开发阵营的主力军。随着微软宣布放弃对 Windows XP 以及以前版本的支持,Windows Vista 的普及势在必行,迟早会安装到普通用户的计算机上。Windows Vista 有很多吸引人的新特性,相信大家不用不知道,一用忘不了。(在翻译 Microsoft Press 的 Windows Vista Inside Out 一书的过程中,我已经深切体会到她的妙处)。作为一名程序员,有必要在第一时间适应在新的操作系统下的编程。历经 15 年,本书也随着 Windows 操作系统的“改朝换代”,升级到第 5 版,即Windows via C/C+。如果您要用
5、C/C+开发 Windows 应用程序,那就不要走弯路,直接让Jeffrey 告诉您如何利用 Windows 的新特性和新函数来编写出高效、优美的 Windows 应用程序。对于本书的学习,谨以史记 孔子世家中孔子学琴一文与大家共勉(请原谅,这里引用了我另一本书的译序,其寓意深刻,忍不住又拿出来与大家分享):孔子学鼓琴师襄子,十日不进。师襄子曰:“可以益矣。”孔子曰:“丘已习其曲矣,未得其数也。”有间,曰:“已习其数,可以益矣。”孔子曰:“丘未得其志也。”有间,曰:“已习其志,可以益矣。”孔子曰:“丘未得其为人也。”有间,有所穆然深思焉,有所怡然高望而远志焉。曰:“丘得其为人,黯然而黑,几然而
6、长,眼如望羊,如王四国,非文王其谁能为此也!”师襄子辟席再拜,曰:“师盖云文王操也。”期望读者朋友也能达到学习的三大境界:学习掌握演奏(编程)的技巧;领会其中的志趣;熟悉乐曲(程序)的作者。翻译过程中,感谢我的家人和朋友的诸多帮助和理解,尤其要感谢我的乖女儿。这个暑假,她的成长令人激赏!最后,欢迎读者指出本书的疏漏和不足之处,如果你对我翻译的部分(16 章)有什么意见和建议,请访问我的博客()指出,那里为我翻译的一些图书开辟了专栏,专门用于和读者们分享勘误和其他有用的信息。周周 靖靖 Beijing 2008 前夕第部分第部分 程序员必读程序员必读本部分内容包括:第 1 章 错误处理第 2 章
7、 字符和字符串处理第 3 章 内核对象第第 1 章章 错误处理错误处理本章内容包括:1.1 定义自己的错误代码1.2 ErrorShow 示例程序在深入讨论 Microsoft Windows 提供的诸多特性之前,应该先理解各个 Windows 函数是如何进行错误处理的。调用 Windows 函数时,它会先验证你传给它的参数,然后再开始执行任务。如果传入的参数无效,或者由于其他原因导致操作无法执行,则函数的返回值将指出函数在某个方面失败了。表 1-1 展示了大多数 Windows 函数使用的返回值的数据类型。表 1-1 常见的 Windows 函数返回值数据类型 数据类型 指出函数调用失败的值
8、 VOID 这个函数不可能失败。只有极少数 Windows 函数的返回值类型为 VOID。BOOL 如果函数失败,返回值为 0;否则,返回值是一个非零值。应避免测试返回值是否为 TRUE;最稳妥的做法是检查它是否不为 FALSE。HANDLE 如果函数失败,则返回值通常为 NULL;否则,HANDLE 将标识一个你可以操纵的对象。请注意这种返回值,因为某些函数会返回为INVALID_HANDLE_VALUE 的一个句柄值,它被定义为1。函数的Platform SDK文 档 清 楚 说 明 了 函 数 是 返 回NULL还 是INVALID_HANDLE_VALUE 来标识失败。PVOID 如果
9、函数调用失败,返回值为 NULL;否则,PVOID 将标识一个数据块的内存地址。LONG/DWORD 这种类型比较棘手。返回计数的函数通常会返回一个 LONG 或 DWORD。如果函数出于某种原因不能对你想要计数的东西进行计数,它通常会返回0 或1(具体取决于函数)。如果要调用一个返回 LONG/DWORD 的函数,务必仔细阅读 Platform SDK 文档,确保你将正确地检查可能出现的错误。如果一个 Windows 函数能返回错误代码,通常有助于我们理解函数调用为什么会失败。Microsoft 编辑了一个列表,其中列出了所有可能的错误代码,并为每个错误代码都分配了一个 32 位的编号。在内
10、部,当一个 Windows 函数检测到错误时,它会使用一个名为“线程本地存储区”(thread-local storage)的机制将恰当的错误代码与“主调线程”(或者说发出调用的线程,即 calling thread)关联到一起(线程本地存储区的详情将在第 21 章讨论)。这个机制使不同的线程能独立运行,不会出现相互干扰对方的错误代码的情况。函数返回时,其返回值会指出已发生一个错误。要查看具体是什么错误,请调用 GetLastError 函数:DWORD GetLastError();它的作用很简单,就是返回由上一个函数调用设置的线程的 32 位错误代码。有了 32 位错误代码之后,接着需要把
11、它转换为更有用的信息。WinError.h 头文件包含了Microsoft 定义的错误代码列表。为便于你体验,下面摘录了其中的一部分:/MessageId:ERROR_SUCCESS/MessageText:/The operation completed successfully./#define ERROR_SUCCESS 0L#define NO_ERROR 0L /dderror#define SEC_E_OK (HRESULT)0 x00000000L)/MessageId:ERROR_INVALID_FUNCTION/MessageText:/Incorrect function.
12、/#define ERROR_INVALID_FUNCTION 1L /dderror/MessageId:ERROR_FILE_NOT_FOUND/MessageText:/The system cannot find the file specified./#define ERROR_FILE_NOT_FOUND 2L/MessageId:ERROR_PATH_NOT_FOUND/MessageText:/The system cannot find the path specified./#define ERROR_PATH_NOT_FOUND 3L/MessageId:ERROR_TO
13、O_MANY_OPEN_FILES/MessageText:/The system cannot open the file./#define ERROR_TOO_MANY_OPEN_FILES 4L/MessageId:ERROR_ACCESS_DENIED/MessageText:/Access is denied./#define ERROR_ACCESS_DENIED 5L 可以看出,每个错误都有三种表示:一个消息 ID(一个可在源代码中使用的宏,用于与GetLastError 的返回值进行比较)、消息文本(描述错误的英文文本)和一个编号(应该避免使用此编号,尽量使用消息 ID)。注意
14、,我只摘录了 WinError.h 头文件的极小一部分,整个文件的长度超过 39 000 行!一个Windows函数失败之后,应该马上调用GetLastError,因为假如又调用了另一个Windows函数,则此值很可能被改写。注意,成功调用的 Windows 函数可能用 ERROR_SUCCESS改写此值。一些 Windows 函数调用成功可能是缘于不同的原因。例如,创建一个命名的事件内核对象时,以下两种情况均会成功:对象实际地完成创建,或者存在一个同名的事件内核对象。应用程序也许需要知道成功的原因。为返回这种信息,Microsoft 选择采用“上一个错误代码”(last error code
15、)机制。所以,当特定函数成功时,你可以调用 GetLastError 来确定额外的信息。对于具有这种行为的函数,Platform SDK 文档会清楚指明能以这种方式使用GetLastError。文档中提供了 CreateEvent 函数的一个例子;如果存在命名的事件,它会返回ERROR_ALREADY_EXISTS。调试程序时,我发现对线程的“上一个错误代码”进行监视是相当有用的。在 Microsoft Visual Studio 中,Microsoft 的调试器支持一个很有用的功能你可以配置 Watch 窗口,让它始终显示线程的上一个错误代码和错误的文本描述。具体的做法是:在 Watch 窗
16、口中选择一行,然后输入$err,hr。来看看图 1-1 的例子。在这个例子中,我已经调用了 CreateFile 函数。该函数返回值为 INVALID_HANDLE_VALUE(1)的一个 HANDLE,指出它无法打开指定文件。但是 Watch 窗口指出,上一个错误代码(也就是调用 GetLastError 函数返回的错误代码)是 0 x00000002。多亏有了,hr 限定符,Watch 窗口进一步指出错误代码 2 是“The system cannot find the file specified”(系统找不到指定文件)。这就是在 WinError.h 头文件中为错误代码 2 列出的消息
17、文本。图 1-1 在 Visual Studio 的 Watch 窗口中使用$err,hr 来查看当前线程的“上一个错误代码”Visual Studio 还搭载了一个很小的实用程序,名为 Error Lookup。利用它,可以将错误代码编号转换为相应的文本描述。如下图所示:如果我在自己写的程序中检测到一个错误,我可能希望向用户显示文本描述,而不是显示一个干巴巴的错误编号。Windows 提供了一个函数,可以将错误代码转换为相应的文本描述。此函数名为 FormatMessage,如下所示:DWORD FormatMessage(DWORD dwFlags,LPCVOID pSource,DWOR
18、D dwMessageId,DWORD dwLanguageId,PTSTR pszBuffer,DWORD nSize,va_list*Arguments);FormatMessage 的功能实际相当丰富,为了构造要向用户显示的字符串,它是首选的一种方式。之所以说它好用,一个原因是它能轻松地支持多种语言(译注:这里的语言是自然语言,比如汉语、英语等等,而不是计算机编程语言)。它能获取一个语言标识符作为参数,并返回那种语言的文本。当然,你首先必须翻译好字符串,并将翻译好的消息表(message table)资源嵌入自己的.exe 或 DLL 模块中。但在此之后,这个函数就能自动选择正确的字符串
19、。ErrorShow 示例程序(参见后文)演示了如何调用这个函数将 Microsoft 定义的错误代码编号转换为相应的文本描述。经常有人问我,Microsoft 是否维护着一个主控列表,其中完整列出了每个 Windows 函数可能返回的所有错误代码。很遗憾,答案是否定的。而且,Microsoft 决不可能提供这样的列表,因为随着新的操作系统版本的产生,很难构建和维护这样的列表。这种列表的问题在于,你可以调用一个 Windows 函数,但在内部,这个函数可能调用另一个函数,后者又可能调用其他函数以此类推。出于众多原因,任何一个函数都可能失败。有时,当一个函数失败时,较高级别的函数也许能够恢复,并
20、继续执行你希望的操作。要创建这种主控列表,Microsoft 必须跟踪每个函数的路径,生成所有可能的错误代码的列表。这是非常难的。而且,随着新版本的操作系统的发布,这些函数的执行路径也可能发生改变。1.1 定义自己的错误代码定义自己的错误代码 前面讲述了 Windows 函数如何向其调用者指出错误。除此之外,Microsoft 还允许将这种机制用于你自己的函数中。假定你要写一个供其他人调用的函数。这个函数可能会因为这样或那样的原因而失败,所以需要向调用者指出错误。为了指出错误,只需设置线程的上一个错误代码,然后令自己的函数返回 FALSE,INVALID_HANDLE_VALUE、NULL 或
21、者其他合适的值。为了设置线程的上一个错误代码,只需调用以下函数,并传递你认为合适的任何 32 位值:VOID SetLastError(DWORD dwErrCode);我会尽量使用 WinError.h 中现有的代码只要代码能很好地反映我想报告的错误。如果WinError.h 中的任何一个代码都不能准确反映一个错误,就可以创建自己的代码。错误代码是一个 32 位数,由表 1-2 描述的几个不同的字段组成。表 1-2 错误代码的不同字段 位:3130 29 28 2716 150 内容 严重性 Microsoft/客户 保留 Facility 代码 异常代码 含义 0=成功 1=信息(提示)2
22、=警告 3=错误 0=Microsoft定义的代码 1=客户定义的代码 必须为 0 前 256 个值由Microsoft保留 Microsoft/客户定义的代码 这些字段将在第 24 章详细讨论。就目前来说,惟一需要注意的重要字段在位 29 中。Microsoft承诺,在它所生成的所有错误代码中,此位将始终为 0。但是,如果要创建你自己的错误代码,就必须在此位放入一个 1。通过这种方式,可以保证你的错误代码绝不会与 Microsoft现在和将来定义的错误代码冲突。注意,Facility 字段非常大,足以容纳 4096 个可能的值。其中,前 256 个值是为 Microsoft 保留的,其余的值
23、可由你自己的应用程序来定义。1.2 ErrorShow 示例程序示例程序 ErrorShow 应用程序(01-ErrorShow.exe),演示了如何得到一个错误代码的文本描述。此应用程序的源代码和资源文件可以在本书示例代码压缩包的 01-ErrorShow 目录中找到,请访问 http:/ 来下载。简单地说,这个应用程序展示了调试器的 Watch 窗口和 Error Lookup 程序是如何工作的(参见前面的两个屏幕截图)。启动程序时,将出现以下窗口。可以在编辑控件中输入任何错误编号。单击 Look Up 按钮后,错误的文本描述将在对话框底部的可滚动窗口中显示。对于这个应用程序,我们惟一感兴
24、趣的是如何调用FormatMessage。下面展示了我如何使用这个函数:/Get the error code DWORD dwError=GetDlgItemInt(hwnd,IDC_ERRORCODE,NULL,FALSE);HLOCAL hlocal=NULL;/Buffer that gets the error message string /Use the default system locale since we look for Windows messages/Note:this MAKELANGID combination has a value of 0 DWORD s
25、ystemLocale=MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL);/Get the error codes textual description BOOL fOk=FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_ALLOCATE_BUFFER,NULL,dwError,systemLocale,(PTSTR)&hlocal,0,NULL);if(!fOk)/Is it a network-related error?HMODULE
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Windows 核心 编程 第五 中文
限制150内