📄 windows核心编程1-8章.txt
字号:
第一部分 程序员必读
第一章 对程序错误的处理
在我们开始介绍Microsoft Windows应该提供的许多特性之前,我们首先必须了解Windows的各个函数是如何进行错误处理的。
当你调用一个Windows函数时,它首先要检验你传递给它的的各个参数的有效性,然后再设法执行它的任务。如果你传递了一个无效参数,
或者由于某种原因它无法执行这项操作,那么该函数就会返回一个值,指明该函数在某种程度上运行失败了。表1-1列出了大多数Windows
函数使用的数据类型的返回值。
表1-1 Windows函数常用的返回值类型
数据类型 表示失败的值
VOID 该函数的运行不可能失败。Windows函数的返回值类型很少
是VOID。
BOLL 如果函数运行失败,那么返回值是0,否则返回的是非0值。最
好对返回值进行测试,以确定它是0还是非0。如果它是TRUE
,则不要测试返回值。
HANDLE 如果函数运行失败,则返回值通常是NULL,否则返回值为
HANDLE,,用于标识你可以操作的一个对象。对于这个返回
值,你应该小心处理,因为有些函数会返回一个句柄
值INVALID_HANDLE_VALUE,它被定义为-1。该函数的
Platform SDK资料将会清楚地说明该函数是返回NULL还
是INVALID_HANDLE_VALID,以便指明函数运行已经失败。
PVOID 如果函数运行失败,则返回值是NULL,否则返回PVOID,以
标识数据块的内存地址。
LONG/DWORD 这是个难以处理的值。返回数量的函数通常返回LONG
或DWORD。如果由于某种原因,函数无法对你想要进行计数
的对象进行计数,那么该函数通常返回0或-1(根据该函数而定)
。如果你调用的函数返回了LONG/DWORD,那么请认真阅
读Platform SDK资料,以确保你能正确检查潜在的错误。
当一个Windows函数返回一个错误代码时,它常常可以用来了解函数为什么会运行失败。
Microsoft公司编译了一个所有可能的错误代码的列表,并且为每个错误代码分配了一个32位的号码。
从系统内部来讲,当一个Windows函数检测到一个错误时,它会使用一个称为线程本地存储器的机制,
将相应的错误代码号码与调用的线程关联起来。(“线程本地存储器”将在第21章中介绍)。
这将使线程能够互相独立地运行,而不会影响各自的错误代码。当函数返回给你时,
它的返回值就能指明一个错误已经发生。若要确定这是个什么错误,请调用GetLastError函数:
见原书P4的程序(1)
该函数只返回线程的32位错误代码。
当你拥有32位错误代码的号码时,你必须将该号码转换成更有用的某种对象。WinError.h头
文件包含了Microsoft公司定义的错误代码的列表。下面我显示了该列表的某些内容,使你能够看到它的大概样子:
见原书P4的程序(2)和P5的程序
你可以看到,每个错误都有3种表示法:即一个消息ID(这是你可以在源代码中使用的一个宏,
以便与GetLastError的返回值进行比较),消息文本(对错误的英文描述)和一个号码(你应
该避免使用这个号码,而应该使用消息ID)。请记住,我只选择了WinError.h头文件中的很少
一部分内容来向你进行展示,整个文件的长度超过21000行。
当Windows函数运行失败时,你应该立即调用GetLastError函数,否则,如果你调用另一个Windows函数,它的值很可能被改写。
说明 GetLastError能返回线程产生的最后一个错误。如果该线程调用的Windows
函数运行成功,那么最后一个错误代码就不被改写,并且不指明运行成功。有少
数Windows函数并不遵循这一规则,并且它会更改最后的错误代码,但是Platform
SDK资料通常指明,当函数运行成功时,该函数会更改最后的错误代码。
Windows
98 许多Windows 98的函数实际上是用Microsoft公司的16位Windows 3.1产
品产生的16位代码来实现的。这种比较老的代码并不通过GetLastError之类函
数来报告错误,而且Microsoft公司并没有在Windows 98中修改16位代码,以
支持这种错误处理方式。对于我们来说,这意味着Windows 98中的许多Win32
函数在运行失败时不能设置最后的错误代码。该函数将返回一个值,指明运行失
败,这样你就能够发现该函数确实已经运行失败。但是你无法确定运行失败的原
因。
有些Windows函数之所以能够成功运行,那是若干个原因产生的结果。例如,创建指明的事件内核对象之所以能够取得成功,原因是你实际上创建了该对象,或者是因为已经存在带有相同名字的事件内核对象。你的应用程序必须知道成功的原因。为了将该信息返回给你,Microsoft公司选择使用最后错误代码机制。这样,当某些函数运行成功时,你就能够通过调用GetLadtError函数来确定其他的一些信息。对于具有这种行为特性的函数来说,Platform SDK资料清楚地说明了GetLastError函数可以这样来使用。请参见该资料,以便找出CreateEvent函数的例子。
当你进行调试的时候,我发现监控线程的最后错误代码是非常有用的。在Microsoft Visual studio 6.0中,Microsoft的调试程序支持一个非常有用的特性,即你可以配置Watch窗口,以便始终都能向你显示线程的最后错误代码的号码和该错误的英文描述。通过选定Watch窗口中的一行,并键入“@err,hr",你就能够做到这一点。观察图1-1,你会看到我已经调用了CreateFile函数。该函数返回INVALID_HANDLE_VALUE(-1)的HANDLE,表示它未能打开指定的文件。但是Watch窗口向我们显示最后错误代码(即如果我调用GetLastErro函数,该函数返回的错误代码)是0x00000002。该Watch窗口又进一步指明错误代码2是指“系统不能找到指定的文件。”你会发现它与WinError.h头文件中的错误代码2所指的字符串是相同的。
图1-1 在Visual Studio 6.0的Watch窗口中键入
“@err,hr",你就可以查看当前线程的最后错误代码。
Visual studio还配有一个小的实用程序,称为Error Lookup。你可以使用Error Lookup将错误代码的号码转换成它的文本描述。
见P7的Error Lookup插图
如果我在我编写的应用程序中发现一个错误,我可能想要向用户显示该错误的文本描述。Windows提供了一个函数,可以将错误代码转换成它的文本描述。该函数称为FormatMessage。请看下面的代码:
见原书P8的程序(1)
FormatMessage函数的功能实际上是非常丰富的,在创建向用户显示的字符串信息时,它是人们首选的函数。该函数之所以有这样大的作用,原因之一是它很容易用多种语言来进行操作。该函数能够检测出用户首选的语言(在Regional Settings Control Panel小应用程序中设定),并返回相应的文本。当然,你首先必须自己转换字符串,然后将已转换的消息表资源嵌入你的.exe文件或DLL模块,不过,这时该函数会选定正确的嵌入对象。ErrorShow示例应用程序(本章后面将加以介绍)展示了如何调用该函数,以便将Microsoft公司定义的错误代码转换成它的文本描述。
有些人常常问我,Microsoft公司是否建立了一个主控列表,以显示每个Windows函数可能返回的所有错误代码。可惜,答案是没有这样的列表,而且Microsoft公司将永远不会建立这样的一个列表。因为在创建系统的新版本时,建立和维护该列表实在太困难了。
建立这样一个列表时存在的问题是,你可以调用一个Windows函数,但是该函数能够在内部调用另一个函数,而这另一个函数又可以调用另一个函数,如此类推。由于各种不同的原因,这些函数中的任何一个函数都可能运行失败。有时,当一个函数运行失败时,较高级的函数对它进行恢复,并且仍然可以执行你想执行的操作。为了创建该主控列表,Microsoft公司必须跟踪每个函数的运行路径,并建立所有可能的错误代码的列表。这项工作很困难。当创建系统的新版本时,这些函数的运行路径就会改变。
1.1 你也能够定义自己的错误代码
好了,我已经说明Windows函数是如何向函数的调用者指明发生的错误。Microsoft公司也使你能够将该机制用于你自己的函数。比如说,你编写了一个你希望其他人调用的函数。你的函数可能因为这样或那样的原因而运行失败,你必须向函数的调用者说明它已经运行失败。
若要指明函数运行失败,你只需要设定线程的最后的错误代码,然后让你的函数返回FALSE,INVALID_HANDLE_VALUE,NULL,或者返回任何合适的信息。若要设定线程的最后错误代码,你只需要调用下面的代码:
见原书P8的程序(2)
请将你认为合适的任何32位号码传递给该函数。我设法使用WinError.h中已经存在的代码,只要该代码能够正确地指明我想要报告的错误即可。如果你认为WinError.h中的任何代码都不能正确地反映该错误的性质,那么你可以创建你自己的代码。错误代码是个32位的数字,它可以划分成下表所示的各个域。
位 31-30 29 28 27-16 15-0
内容 严重性 Microsoft/ 保留 设备代码 异常代码
客户
含义 0=成功 0=Microsoft 必须是0 由Microsoft 由Microsoft/
1=供参考 公司定义的 公司定义 客户定义
2=警告 代码
3=错误 1=客户定义
的代码
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -