对程序错误的处理

——Windows核心编程学习手札之一

函数被调用执行时,先检验传递给它的各个参数的有效性,后执行任务。函数执行中若因参数无效或因某种原因导致无法正常完成函数任务,那么操作系统会返回一个值,以提示函数运行失败。这个思想可归纳为:函数错误提示的透明,同样适用于个人编写的函数,在函数执行任务的每个关键点设置错误提示并返回。对所返回的错误代码或提示对于正确定位和调试函数失败点是非常有用的。

Microsoft给出了一个所有可能的错误代码列表,并为每个错误代码分配一个32位的号码。Windows函数常用的返回值类型有VOID/BOOL/HANDLE/PVOID/LONG/DWORD,基于此,个人编写的程序也可以维护一张自己的错误代码列表,并设定返回类型。

当函数检测到错误时,会使用线程本地存储器(thread-local storage)机制,将相应的错误代码号码与调用的线程关联起来,这使线程能够互相独立地运行,而不影响各自的错误代码。Windows函数返回时,其返回值就能指明一个错误已经发生。若要确定发生的错误是什么,可调用函数:

DWORD GetLastError();

该函数返回线程的32位错误代码。有了32位错误代码的号码,便可以将该号码转换成更有用的信息。WinError.h头文件包含了Microsoft定义的错误代码列表。当函数运行失败时,应立即调用GetLastError函数,因为如果调用另一个Windows函数,它的值很可能被改写。

有些函数可能返回了正确的代码,但并不表明函数实际运行成功,其中有很多原因。如,创建指明的事件内核对象之所以成功,是因为实际上已经创建了该对象或已经存在带有相同名字的事件内核对象。

程序调试中,对线程的最后错误代码进行提取和分析是非常有用的。在Microsoft Visual studio6.0中,Microsoft的调试程序可以配置Watch窗口,以便始终显示线程的最后错误代码及其英文描述。操作是在Watch窗口中的一行输入“@err,hr”即可。Visual studio还提供一个实用程序Error Lookup可以将错误代码的号码转换成相应的文本描述。个人在用Microsoft Visual C++6.0开发的单元调试中经常用到Watch窗口和Error Lookup来查看最后错误代码,当然Watch窗口的作用还不仅仅是查看错误代码及其描述。

同时Windows提供一个函数FormatMessage可以将错误代码转换成它的文字描述。

DWORD FormatMessage(

DWORD dwFlags,

LPCVOID pSource,

DWORD dwMessageID,

DWORD dwLanguageID,

PTSTR pszBuffer,

DWORD nSize,

va_list *Arguments);

FormatMessage函数能够检测出用户首选的语言,并返回相应的文本。对于Microsoft是否应该建立一个主控列表,以显示每个Windows函数可能返回的所有错误代码,可从下面两方面来否定:一是创建新系统版本时,建立和维护该主控列表太困难;二是函数在内部调用另一函数,而这一函数同样内部调用其他函数,如此类推,其中任何一个函数运行失败,调用函数都可能恢复并执行工作,而Microsoft需要跟踪每个函数的运行路径,并建立所有可能的错误代码列表。

实际上我们在使用Win32的开发环境中,调用Windows函数可以利用GetLastError函数来知道所调用的Windows函数错误所指,而对自己的函数则需要自己编写错误代码,或说维护自己的错误代码列表,以便与Windows函数一样可以定位错误。换句话说,可以将Windows函数告诉调用者指明发生的错误的机制用于自己的函数体系。一方面可以利用Windows已有的错误代码,在某个函数可能出现错误的地方设置最后错误代码并返回,可采用Windows函数:

VOID SetLastError(DWORD dwErrCode);

那么可以用WinError.h中代码,只要该代码可能说明错误思想并让调用者理解即可,如想反映句柄无效,可以设置错误代码为6,错误代码是32位的数字。

另一方面你认为有必要维护自己的错误列表,或WinError.h中的错误不足以反映你程序的错误性质,可以创建自己的代码,Microsoft规定,该公司建立的所有错误代码在第29位的信息位使用0,用户自己创建的使1,可以确保自己的错误代码与Microsoft不冲突。具体错误代码域可如下:

位    31~30        29                28          27~16          15~0

内容    严重性       Microsoft/客户      保留        设备代码        异常代码

含义    0=成功       0=Microsoft公司    必须是0    由Microsoft     由Microsoft

公司定义      公司定义

定义的代码

1=供参考     1=客户定义代码

2=警告

3=错误

这里模拟了Microsoft Visual C6.0中Error Lookup程序,利用MFC基于对话框的程序实现,主要实现代码如下:

void CErrorShowDlg::OnBtnLookUp()

{

// TODO: Add your control notification handler code here

//Get the error code

BOOL bRetErrorCode;

DWORD dwErrorCode=GetDlgItemInt(IDC_EdtErrorCode,&bRetErrorCode,FALSE);

if(!bRetErrorCode)//无值

{

AfxMessageBox("输入错误的代码!");

return;

}

//Get the error code's textual description

HLOCAL hLocal=NULL;

BOOL bRetErrorText=FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,

NULL,dwErrorCode,MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),

(LPTSTR)&hLocal,0,NULL);

if(hLocal != NULL)

{

SetDlgItemText(IDC_EdtErrorShow,(LPCTSTR)LocalLock(hLocal));

LocalFree(hLocal);

}else

SetDlgItemText(IDC_EdtErrorShow,TEXT("Error Number not found!"));

}

如非  2008-11-22

对程序错误的处理——Windows核心编程学习手札之一相关推荐

  1. 在应用程序中使用虚拟内存——Windows核心编程学习手札之十五

    在应用程序中使用虚拟内存 --Windows核心编程学习手札之十五 Windows提供了3种进行内存管理的方法: 1)  虚拟内存,最适合用来管理大量对象或结构数组: 2)  内存映射文件,最适合用来 ...

  2. 未处理异常和C++异常——Windows核心编程学习手札之二十五

    未处理异常和C++异常 --Windows核心编程学习手札之二十五 当一个异常过滤器返回EXCEPTION_CONTINUE_SEARCH标识符时是告诉系统继续上溯调用树,寻找另外的异常过滤器,但当每 ...

  3. 异常处理程序和软件异常——Windows核心编程学习手札之二十四

    异常处理程序和软件异常 --Windows核心编程学习手札之二十四 CPU负责捕捉无效内存访问和用0除一个数值这种错误,并相应引发一个异常作为对错误的反应,CPU引发的异常称为硬件异常(hardwar ...

  4. 结束处理程序——Windows核心编程学习手札之二十三

    结束处理程序 --Windows核心编程学习手札之二十三 使用SEH可以只关注程序要完成任务,而运行中发生的错误,系统将会发现并通知.Windows引入SHE是为了便于操作系统的开发,使用SHE所造成 ...

  5. 线程本地存储器——Windows核心编程学习手札之二十一

    线程本地存储器 --Windows核心编程学习手札之二十一 C/C++运行期库使用线程本地存储器,运行期库是在多线程应用程序出现前设计的,因此运行期库里的大多数函数是用于单线程应用程序的.函数strt ...

  6. DLL的高级操作技术——Windows核心编程学习手札之二十

    DLL的高级操作技术 --Windows核心编程学习手札之二十 显示加载DLL模块: HINSTANCE LoadLibrary(PCTSTR pszDLLPathName); HINSTANCE L ...

  7. 内存映射文件——Windows核心编程学习手札之十七

    内存映射文件 --Windows核心编程学习手札之十七 与虚拟内存一样,内存映射文件保留地址空间,并将物理存储器提交给该区域,差别在于所提交的物理存储器是磁盘上有文件存在的空间,而非系统的页文件,一旦 ...

  8. 线程基础知识——Windows核心编程学习手札系列之六

    线程基础知识 --Windows核心编程学习手札系列之六 线程与进程一样由两部分构成:一是线程的内核对象,操作系统用它来对线程实施管理,也是系统用来存放线程统计信息的地方:二是线程堆栈,用于维护线程在 ...

  9. 进程——Windows核心编程学习手札系列之四

    进程 --Windows核心编程学习手札系列之四 进程是一个正在运行的程序的实例,有两个部分组成:一个是操作系统用来管理进程的内核对象,内核对象是系统用来存放关于进程的统计信息的地方:另一个是地址空间 ...

最新文章

  1. Angular自学笔记(?)依赖注入
  2. leetcode 40. 组合总和 II 思考分析
  3. anroid抓包工具tcpdump的用法
  4. luogu P1892 团伙
  5. slf4j-api slf4j-log4j12以及log4j之间的关系
  6. 博客平台、Markdown编辑器与hexo admin简介
  7. RocketMQ 下载、安装与 单机启动
  8. Extjs中EditorGridPanel修改并获取数据的两种方式
  9. Kafka面试题(附答案)
  10. mysql 索引间隙锁_mysql innodb间隙锁示例
  11. Qt5 和 OpenCV4 计算机视觉项目:1~5
  12. 外置MOS 开关型 PWM调光 降压恒流驱动芯片
  13. lgv50怎么进入fastboot模式_fastboot知识扫盲 高级刷机方式fastboot模式怎么进入?
  14. EXCEL如何设置,使表格能自动调整列宽以适应文字长度
  15. 新手学习【菜鸟教程】Python CGI编程的几个坑(Windows系统)
  16. Linux shell编程自动化运维,三剑客之awk原理,语法,内部变量,格式化输出,模式和动作 详细解析
  17. 虚拟机ERP服务器配置,erp硬件推荐部署方案v.pdf
  18. spring配置读取properties文件
  19. 物联网开源框架 Thingsboard 使用总结
  20. 国内工业控制系统标准概述

热门文章

  1. 手动制造报错_一个订单管理系统帮你轻松应对复杂的生产订单管理
  2. linux切换桌面环境bug,GNOME 3.32.2桌面环境发布,最新的bug和安全修复
  3. 怎么用git将本地代码上传到远程服务器_TortoiseGit将本地git仓库上传到远程git服务器方法...
  4. linux ssh客户端工具
  5. ios 中ARC与非ARC的转换
  6. springcloud系列四 搭建服务模块重点讲解
  7. bzoj 2119 股市的预测 —— 枚举关键点+后缀数组
  8. [BZOJ4994] [Usaco2017 Feb]Why Did the Cow Cross the Road III(树状数组)
  9. pivot 用order by 语句排序时,老是超时的解决办法,超Easy
  10. 《那些年啊,那些事——一个程序员的奋斗史》——59