当前位置:  编程技术>c/c++/嵌入式

深入HRESULT与Windows Error Codes的区别详解

    来源: 互联网  发布时间:2014-10-14

    本文导语:  在用C++来开发Windows程序时,经常看到下面的判断情况: 代码如下:HRESULT hr = ::RegCreateKeyEx(hk, szKeyPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE, NULL, &hk, NULL);if (SUCCEEDED(hr)) {在代码中,使用SUCCEEDED宏来判断函数RegCreateKeyEx()函数的返...

在用C++来开发Windows程序时,经常看到下面的判断情况:
代码如下:

HRESULT hr = ::RegCreateKeyEx(hk, szKeyPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE, NULL, &hk, NULL);
if (SUCCEEDED(hr))
{

在代码中,使用SUCCEEDED宏来判断函数RegCreateKeyEx()函数的返回值。
有些程序员认为RegCreateKeyEx返回0的时候就是成功,而S_OK就是0,所以就习惯性的用SUCCEEDED宏来做判断。
还有些人用下面的方法判断,看起来更严谨一些:
代码如下:

HRESULT hr = ::RegCreateKeyEx(hk, szKeyPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE, NULL, &hk, NULL);
if (S_OK == hr)
{

确实,第2种更严谨一些,至少不会造成大问题,而第1中则完全是一个大Bug,这个bug在正常情况下是没有问题的。但一旦有问题,你也发现不了。
错在哪里呢?听我下面来介绍。

SUCCEEDED
先看下这个宏的定义(WinError.h):
代码如下:

//
// Generic test for success on any status value (non-negative numbers
// indicate success).
//
#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)

从这里可以看出,它就是把hr转换成HRESULT类型,然后做了下是否大于0的判断。注释中也说明:但值为非负数时表示成功。
也就是说,只要HRESULT是大于等于0的值,它就认为是成功的。

HRESULT
再来看下HRESULT的定义(winnt.h):
代码如下:

// Component Object Model defines, and macros
#ifndef _HRESULT_DEFINED
#define _HRESULT_DEFINED
typedef LONG HRESULT;
#endif // !_HRESULT_DEFINED

哦,原来HRESULT就是一个Long型的整数。
在MSDN中,可以查到更加详细的资料:

如上图,HRESULT是一个4字节的Long型,总共32位。其中:
第31位是s位,即符号位,因为HRESUlT格式规定所有成功都是正的整数,失败的值都是负数
第30位是r位,是保留位,但n位(28位)没有设置时,它必须是0;如果n位使用了,则和s位一起来标识NTSTATUS的值。
第29位是c位,表示Custom,即自定义位,如果是微软定义的返回值,则该位为0;如果是自定义的,则该位为1.
第28位是n位,表示NTSTATUS,值为0的话可以把NTSTATUS值映射为一个HRESULT值。
第27位是x位,保留位,必须为0.
第26位到第16位是Facility,用11位来表示错误来源,比如
代码如下:

FACILITY_WINDOWS 表示来自Windows子系统

第15位到第1位是Code位,用来保存错误值。

从这里可以看出,只有最后面的2个字节是用来表示返回值的其它的都是辅助信息,它主要用于COM函数的返回值。

常见HRESULT值

Name Description Value S_OK 操作成功 0x00000000 S_FALSE 操作成功,但是有问题 0x00000001L E_ABORT 操作中止 0x80004004 E_ACCESSDENIED 拒绝访问 0x80070005 E_FAIL 未知错误 0x80004005
注意:除了S_OK外,还有一个S_FALSE,它也属于成功。
所以,微软为了方便大家使用,专门提供了SUCCEEDED宏和FAILED宏来方便大家做判断。
到这里,大家明白了吧:SUCCEEDED宏是用来判断COM中的函数执行是否成功用的,失败为负数,成功为0和正数。

Windows Error Code
前面的代码中我们调用了一个Windows API:
代码如下:

:RegCreateKeyEx(hk, szKeyPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE, NULL, &hk, NULL);

这个API的声明是:
代码如下:

LONG WINAPI RegCreateKeyEx(
  __in        HKEY hKey,
  __in        LPCTSTR lpSubKey,
  __reserved  DWORD Reserved,
  __in_opt    LPTSTR lpClass,
  __in        DWORD dwOptions,
  __in        REGSAM samDesired,
  __in_opt    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  __out       PHKEY phkResult,
  __out_opt   LPDWORD lpdwDisposition
);

从MSDN中知道,它成功时返回的是ERROR_SUCCESS,其它值则是失败,其它值就是类似GetLastError的错误码。这些错误码就是Windows Error Code。

Windows Error Codes
微软在WinError.h定义了大量的Windows Error Codes,这种错误码范围是0x0000~0xFFFF,即2个字节,但没限定死2个字节,也可以用4个字节来保存。在Windows API中,大量的使用了这种错误码。比如上面的注册表API,它的返回值就是这种错误码。
这种错误码还有个特点是微软为这些错误码定义了比较详细的可阅读的描述信息,它可以通过FormatMessage函数来获得,在中文环境下,显示的是翻译后的中文。

Windows Error Codes 除了ERROR_SUCCESS外,都是正数,也就是不能用SUCCEEDED宏来判断,因为这个宏只判断是不是非负数,对于它而言,所有的Windows Error Codes都是成功的。

常见的Windows Error Codes

Win32 error codes Description 0x00000000
ERROR_SUCCESS The operation completed successfully. 0x00000000
NERR_Success The operation completed successfully. 0x00000001
ERROR_INVALID_FUNCTION Incorrect function. 0x00000002
ERROR_FILE_NOT_FOUND The system cannot find the file specified. 0x00000003
ERROR_PATH_NOT_FOUND The system cannot find the path specified. 0x00000004
ERROR_TOO_MANY_OPEN_FILES The system cannot open the file. 0x00000005
ERROR_ACCESS_DENIED Access is denied.

所以前面的代码中,混淆了HRESULT和Windows Error Code,特别是第一种代码,当注册表失败时它也会判断为成功,第2种因为两个都是0,碰巧不会出问题,但是建议还是不要这么混用。

总结





    
 
 

您可能感兴趣的文章:

  • 深入JDBC sqlserver连接写法的详解
  • 深入mysql YEAR() MONTH() DAYOFMONTH()日期函数的详解
  • 深入SQLServer中ISNULL与NULLIF的使用详解
  • 深入C++可见性与生命期的区别详解
  • 深入mysql并发插入优化详解
  • 深入android Unable to resolve target 'android-XX'详解
  • 深入MYSQL字符数字转换的详解
  • 深入SQL Server中定长char(n)与变长varchar(n)的区别详解
  • 深入C#任务管理器中应用程序选项隐藏程序本身的方法详解
  • 深入Windows下的回车是回车换行(rn)还是换行回车(nr)的详解
  • 深入分析NTFS中文件被锁定导致Process.Start失败的详解
  • 深入C# 内存管理以及优化的方法详解
  • 深入c# Func委托的详解
  • 深入分析Java内存区域的使用详解
  • Informatica bulk与normal模式的深入详解
  • 深入JAVA对象深度克隆的详解
  • 深入mysql存储过程中表名使用参数传入的详解
  • 深入SQL截取字符串(substring与patindex)的详解
  • 深入Java不可变类型的详解
  • 深入Android开发FAQ的详解
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • Docker支持更深入的容器日志分析
  • 关于《深入浅出MFC》
  • Linux有没有什么好的高级的书,我要深入,
  • 深入理解linux内核
  • [100分]有没有关于binutils的深入的资料?或者深入底层的资料?
  • 深入理解PHP内核 TIPI
  • 想深入学习Java应该学习哪些东西
  • 哪位有《JSP深入编程》电子版?
  • 想要深入学习LINUX该学什么?
  • 100分求:哪儿有《深入理解linux内核》可供下哉!
  • 如何深入Linux的内核学习?
  • U-BOOT得掌握到什么程序,用不用深入去学
  • 想深入了解操作系统该怎么做
  • 前一阵子学习了shell脚本,如果想深入点了解linux可以看什么书呢
  • 问一个《深入理解计算机系统》中的问题
  • 深入多线程之:深入分析Interlocked
  • ##想买书深入学习linux下的编程,请指教
  • 深入oracle特定信息排序的分析
  • 深入分析C中不安全的sprintf与strcpy
  • 哪儿有下载《深入理解Linux内核》这本书?(中文)
  • 请问JFC是否有必要深入研究?


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3