程序员 必读

2017-02-11  本文已影响0人  热爱生活的男孩子

     对程序错误的处理在开始介绍Microsoft Windows 的特性之前,必须首先了解Wi n d o w s的各个函数是如何进行错误处理的。当调用一个Wi n d o w s函数时,它首先要检验传递给它的的各个参数的有效性,然后再设法执行任务。如果传递了一个无效参数,或者由于某种原因无法执行这项操作,那么操作系统就会返回一个值,指明该函数在某种程度上运行失败了。表 1 - 1列出了大多数Wi n d o w s函数使用的返回值的数据类型。表1-1 Wi n d o w s函数常用的返回值类型数 据 类 型 表示失败的值V O I D 该函数的运行不可能失败。Wi n d o w s函数的返回值类型很少是V O I DB O O L 如果函数运行失败,那么返回值是0,否则返回的是非0值。最好对返回值进行测试,以确定它是0还是非0。不要测试返回值是否为T R U EH A N D L E 如果函数运行失败,则返回值通常是N U L L,否则返回值为H A N D L E,用于标识你可以操作的一个对象。注意,有些函数会返回一个句柄值I N VALID_ HANDLE_VA L U E,它被定义为- 1。函数的Platform SDK文档将会清楚地说明该函数运行失败时返回的是N U L L还是I N VA L I D _ H A N D L E _ VA L I DP V O I D 如果函数运行失败,则返回值是N U L L,否则返回P V O I D,以标识数据块的内存地址L O N G / D W O R D 这是个难以处理的值。返回数量的函数通常返回 L O N G或D W O R D。如果由于某种原因,函数无法对想要进行计数的对象进行计数,那么该函数通常返回 0或- 1(根据函数而定)。如果调用的函数返回了L O N G / D W O R D,那么请认真阅读Platform SDK文档,以确保能正确检查潜在的错误

一个Wi n d o w s函数返回的错误代码对了解该函数为什么会运行失败常常很有用。 M i c r o s o f t公司编译了一个所有可能的错误代码的列表,并且为每个错误代码分配了一个 3 2位的号码。从系统内部来讲,当一个Wi n d o w s函数检测到一个错误时,它会使用一个称为线程本地存储器(thread-local storage)的机制,将相应的错误代码号码与调用的线程关联起来(线程本地存储器将在第2 1章中介绍)。这将使线程能够互相独立地运行,而不会影响各自的错误代码。当函数返回时,它的返回值就能指明一个错误已经发生。若要确定这是个什么错误,请调用G e t L a s t E r r o r函数:该函数只返回线程的3 2位错误代码。当你拥有3 2位错误代码的号码时,必须将该号码转换成更有用的某种对象。 Wi n E r r o r. h头文件包含了M i c r o s o f t公司定义的错误代码的列表。下面显示了该列表的某些内容,使你能够看到它的大概样子:

免使用这个号码,可使用消息I D)。请记住,这里只显示了Wi n E r r o r. h头文件中的很少一部分内容,整个文件的长度超过2 1 0 0 0行。当Wi n d o w s函数运行失败时,应该立即调用G e t L a s t E r r o r函数。如果调用另一个Wi n d o w s函数,它的值很可能被改写。注意 G e t L a s t E r r o r能返回线程产生的最后一个错误。如果该线程调用的Wi n d o w s函数运行成功,那么最后一个错误代码就不被改写,并且不指明运行成功。有少数Wi n d o w s函数并不遵循这一规则,它会更改最后的错误代码;但是 Platform SDK文档通常指明,当函数运行成功时,该函数会更改最后的错误代码。Wi n d o w s 9 8 许多Windows 98的函数实际上是用M i c r o s o f t公司的1 6位Windows 3.1产品产生的1 6位代码来实现的。这种比较老的代码并不通过 G e t L a s t E r r o r之类的函数来报告错误,而且M i c r o s o f t公司并没有在Windows 98中修改1 6位代码,以支持这种错误处理方式。对于我们来说,这意味着Windows 98中的许多Wi n 3 2函数在运行失败时不能设置最后的错误代码。该函数将返回一个值,指明运行失败,这样你就能够发现该函数确实已经运行失败,但是你无法确定运行失败的原因。有些Wi n d o w s函数之所以能够成功运行,其中有许多原因。例如,创建指明的事件内核对象之所以能够取得成功,是因为你实际上创建了该对象,或者因为已经存在带有相同名字的事件内核对象。你应搞清楚成功的原因。为了将该信息返回, M i c r o s o f t公司选择使用最后错误代码机制。这样,当某些函数运行成功时,就能够通过调用 G e t L a d t E r r o r函数来确定其他的一些信息。对于具有这种行为特性的函数来说, Platform SDK文档清楚地说明了G e t L a s t E r r o r函数可以这样使用。请参见该文档,找出C r e a t e E v e nt函数的例子。进行调试的时候,监控线程的最后错误代码是非常有用的。在Microsoft Visual studio 6.0中,M i c r o s o f t的调试程序支持一个非常有用的特性,即可以配置 Wa t c h窗口,以便始终都能显示线程的最后错误代码的号码和该错误的英文描述。通过选定 Wa t c h窗口中的一行,并键入“@ e r r, h r”,就能够做到这一点。观察图1 - 1,你会看到已经调用了C r e a t e F i l e函数。该函数返回I N VA L I D _ H A N D L E _ VA L U E(- 1)的H A N D L E,表示它未能打开指定的文件。但是Wa t c h窗口向我们显示最后错误代码(即如果调用 G e t L a s t E r r o r函数,该函数返回的错误代码)是0 x 0 0 0 0 0 0 0 2。该Wa t c h窗口又进一步指明错误代码2是指“系统不能找到指定的文件。”你会发现它与Wi n E r r o r. h头文件中的错误代码2所指的字符串是相同的。图1-1 在Visual Studio 6.0的Wa t c h窗口中键入“@ e r r, h r”,就可以查看当前线程的最后错误代码F o r m a t M e s s a g e函数的功能实际上是非常丰富的,在创建向用户显示的字符串信息时,它是首选函数。该函数之所以有这样大的作用,原因之一是它很容易用多种语言进行操作。该函数能够检测出用户首选的语言(在Regional Settings Control Panel小应用程序中设定),并返回相应的文本。当然,首先必须自己转换字符串,然后将已转换的消息表资源嵌入你的 . e x e文件或D L L模块中,然后该函数会选定正确的嵌入对象。 E r r o r S h o w示例应用程序(本章后面将加以介绍)展示了如何调用该函数,以便将M i c r o s o f t公司定义的错误代码转换成它的文本描述。有些人常常问我,M i c r o s o f t公司是否建立了一个主控列表,以显示每个Wi n d o w s函数可能返回的所有错误代码。可惜,回答是没有这样的列表,而且 M i c r o s o f t公司将永远不会建立这样的一个列表。因为在创建系统的新版本时,建立和维护该列表实在太困难了。建立这样一个列表存在的问题是,你可以调用一个 Wi n d o w s函数,但是该函数能够在内部调用另一个函数,而这另一个函数又可以调用另一个函数,如此类推。由于各种不同的原因,这些函数中的任何一个函数都可能运行失败。有时,当一个函数运行失败时,较高级的函数对它进行恢复,并且仍然可以执行你想执行的操作。为了创建该主控列表, M i c r o s o f t公司必须跟踪每个函数的运行路径,并建立所有可能的错误代码的列表。这项工作很困难。而且,当创建系统的新版本时,这些函数的运行路径还会改变。1.1 定义自己的错误代码前面已经说明 Wi n d o w s函数是如何向函数的调用者指明发生的错误,你也能够将该机制用于自己的函数。比如说,你编写了一个希望其他人调用的函数,你的函数可能因为这样或那样的原因而运行失败,你必须向函数的调用者说明它已经运行失败。若要指明函数运行失败,只需要设定线程的最后的错误代码,然后让你的函数返回FA L S E、I N VA L I D _ H A N D L E _ VA L U E、N U L L或者返回任何合适的信息。

上一篇下一篇

猜你喜欢

热点阅读