教你看懂C++类库函数定义之一---HRESULT 宏
一切从一个C++ 类库头文件开始,现在在做一个C++的项目,期间用到一个开源的界面库DUILib(类似MFC),这个东西还不错能很容易的写出漂亮的界面,比如QQ的界面,可以去下载下来研究研究,地址:http://code.google.com/p/duilib/
废话不多说,我比较困扰的是UIWebBrowser.h这个头文件,虽然是C++写的,但里面包含太多大学C++课本以外的东西,第一遍看下来跟看天书一样,里面有很多的不惑,接下来我们一个一个解开。
首先看一下这个函数定义:
virtual HERSULT STDMETHODCALLTYPE GetTypeInfoCount( __RPC__out UINT *pctinfo);
这一篇详细介绍 HERSULT
在用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,碰巧不会出问题,但是建议还是不要这么混用。
总结

参考资料
[MS-ERREF]: Windows Error Codes
http://msdn.microsoft.com/en-us/library/cc231196.aspx
HRESULT
http://msdn.microsoft.com/en-us/library/cc231198.aspx
2.2 Win32 Error Codes
http://msdn.microsoft.com/en-us/library/cc231199(v=PROT.10).aspx
2.3 NTSTATUS
http://msdn.microsoft.com/en-us/library/cc231200(v=PROT.10).aspx
Common HRESULT Values
http://msdn.microsoft.com/en-us/library/aa378137(VS.85).aspx
RegCreateKeyEx Function
http://msdn.microsoft.com/en-us/library/ms724844(VS.85).aspx
教你看懂C++类库函数定义之一---HRESULT 宏的更多相关文章
- 教你看懂C++类库函数定义之二---STDMETHOD介绍
一切从一个C++ 类库头文件开始,现在在做一个C++的项目,期间用到一个开源的界面库DUILib(类似MFC),这个东西还不错能很容易的写出漂亮的界面,比如QQ的界面,可以去下载下来研究研究,地址:h ...
- 教你看懂C++类库函数定义之三---_stdcall
一切从一个C++ 类库头文件开始,现在在做一个C++的项目,期间用到一个开源的界面库DUILib(类似MFC),这个东西还不错能很容易的写出漂亮的界面,比如QQ的界面,可以去下载下来研究研究,地址:h ...
- 教你看懂Code128条形码
首 页 条码控件 条码技术 条码新闻 合作伙伴 联系我们 常见问题 电话:010-84827961 当前位置:条形码控件网 > 条形码控件技术文章 > >正文 教你看懂C ...
- 看懂c/c++ 函数、指针、数组定义
读懂 函数 + 指针 + 数组 c语言运算符机器优先级,看这里 结合运算符优先级,我们试着读懂函数和指针 优先级简单看 表达式提升():一级优先 函数():二级优先 数组[]:二级优先 指针定义*:三 ...
- emmm 深入浅出教你看懂现代金融游戏
3303只信仰公平[网易陕西省西安网友]1 比特币是骗人的.你们都被“现代帼家纸币”概念茜脑了,而且茜的很彻底,所以你们看不透比特币的骗局.简单来说,现代纸币是“空气纸”,比特币是“空气币(空气数据) ...
- 教你看懂网上流传的60行JavaScript代码俄罗斯方块游戏
早就听说网上有人仅仅用60行JavaScript代码写出了一个俄罗斯方块游戏,最近看了看,今天在这篇文章里面我把我做的分析整理一下(主要是以注释的形式). 我用C写一个功能基本齐全的俄罗斯方块的话,大 ...
- 教Alexa看懂手语,不说话也能控制语音助手
Alexa.Siri.小度……各种语音助手令人眼花缭乱,但这些设备多是针对能力健全的用户,忽略了听.说能力存在障碍的人群.本文作者敏锐地发现了这一 bug,并训练亚马逊语音助手 Alex 学会识别美式 ...
- 手把手教你看懂并理解Arduino PID控制库——引子
介绍 本文主要依托于Brett Beauregard大神针对Arduino平台撰写的PID控制库Arduino PID Library及其对应的帮助博客Improving the Beginner’s ...
- 教你看懂邮件头信息<转载>
MIME对于邮件系统的扩展是巨大的,因为在MIME出现以前,信件内容如果要包括声音和动画,就必须把它变为ASCII码或把二进制的信息变成可以传送的编码标准,而接收方必须经过解码才可以获得声音和图画信息 ...
随机推荐
- Port 8081 already in use, packager is either not running or not running correctly
运行 react_native 时发生这个错误,解决办法 关掉端口8081对应的进程 1.打开终端,输入命令:lsof -i:8081 2.此时提示: COMMAND PID USER ...
- linux驱动调试--段错误之oops信息分析
linux驱动调试--段错误之oops信息分析 http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29401328&id= ...
- mysql支持emoji解决办法
mysql显示不了emoji表情或者显示??,原因这里不解释,直接说解决办法.(主要就是修改utf8mb4) 1.修改表 ALTER TABLE `TABLE_NAME` CHARACTER SET ...
- 快速配置SSH证书登录
环境: 在 CentOS 5/6/7.RHEL 5/6/7 和 Oracle Linux 6/7 上测试通过 使用 ssh-key-gen 命令生成公钥和私钥 用 ssh-copy-id 命令将公钥复 ...
- Startup 和 Middleware(中间件)
Startup 和 Middleware(中间件) ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Con ...
- Mac平台编译mupdf-qt的开源项目
How to compile mupdf-qt Compile on Linux Install tools and thirdparty libraries You should install s ...
- 依赖和用jar包得区别
有个项目,需要用到第三方开发的一个jar文件,我先是把生成的jar文件直接拷贝到我的项目的libs目录下,项目自动加载了引用的jar包,在java文件中使用也没有问题,但是由于引用的jar文件中有自定 ...
- JRebel 6 破解版及使用方法
最近更新到jrebel6.2.1了,我自己做了个技术分享的微信公众号(茶爸爸),有心的朋友可以来这里一起学习 云盘下载链接: http://pan.baidu.com/s/1bnGzMUF 配置: - ...
- 点菜系统 pickview的简单实用
使用pickview的时候多想想tableview的使用,观察两者的相同之处 pickview的主要用途用于选择地区 生日年月日 和点餐 示例代码 简单的pickview点餐系统// ViewC ...
- MVC自定义AuthorizeAttribute实现权限管理
[转]MVC自定义AuthorizeAttribute实现权限管理 原文载自:小飞的DD http://www.cnblogs.com/feiDD/articles/2844447.html 网站的权 ...