C++零食:HRESULT 与 Windows Error Codes 不是一回事

在用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中,可以查到更加详细的资料:

image

如上图,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值

NameDescriptionValue
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 codesDescription
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,碰巧不会出问题,但是建议还是不要这么混用。

 

总结

hresultAndWinErrorCodes

 

参考资料

[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

转载于:https://www.cnblogs.com/greenerycn/archive/2010/08/30/hresult_and_win_error_codes.html

在用户使用 T-Con 串口工具时,遇到的异常类型为 `Microsoft C++ 异常: winrt::hresult_error`,错误地址为 `0x751FDD42`。这种异常通常 Windows 运行时 (WinRT) 的错误处理机制相关,并且可能涉及 HRESULT 错误代码的抛出。 ### 原因分析 - **HRESULT 错误代码**:`winrt::hresult_error` 是一种封装了 HRESULT 错误代码的 C++ 异常类型,用于在 WinRT API 调用失败时通知调用者。当程序调用 WinRT 函数或方法时,如果返回失败的 HRESULT(例如 `E_INVALIDARG`、`E_OUTOFMEMORY` 等),则可能会触发此异常。 - **异常来源**:异常地址 `0x751FDD42` 可能指向系统模块中的某个位置,表明该异常是由 WinRT 或底层 COM 子系统引发的。这通常意味着在调用 WinRT API 时发生了错误,例如传递了无效参数、资源不足或其他运行时问题 [^3]。 - **跨 ABI 边界的问题**:由于 WinRT 使用 COM 作为其底层机制,任何从实现层抛出的 C++ 异常都会被转换为 HRESULT 错误代码,并通过 `winrt::to_hresult()` 函数进行处理。如果异常未被正确捕获和转换,则可能导致崩溃或未处理的异常 [^4]。 --- ### 解决方法 1. **检查 WinRT API 调用**: - 检查 T-Con 工具中是否调用了 WinRT API 或 C++/WinRT 方法。 - 确保所有 WinRT 函数调用都正确处理了返回值,并在必要时使用 `winrt::check_hresult` 或 `winrt::check_bool` 来验证操作的成功否 [^3]。 - 示例代码: ```cpp winrt::check_hresult(SHCreateItemFromParsingName(L"example.txt", nullptr, IID_IShellItem, reinterpret_cast<void**>(&shellItem))); ``` 2. **使用异常捕获机制**: - 在 WinRT 编程中,建议将关键代码包裹在 try-catch 块中,并使用 `winrt::to_hresult()` 将异常转换为 HRESULT 以避免未处理的异常 [^4]。 - 示例代码: ```cpp HRESULT DoWork() noexcept { try { // Perform WinRT operations here return S_OK; } catch (...) { return winrt::to_hresult(); } } ``` 3. **调试和日志记录**: - 启用调试器(如 Visual Studio Debugger)来捕获异常发生的具体位置。 - 添加日志输出,记录 WinRT API 返回的 HRESULT 值,以便定位问题根源。 - 使用 `winrt::hresult_error::code()` 方法获取具体的 HRESULT 错误码,并参考 Microsoft 文档查找对应的含义 [^1]。 4. **更新依赖项和库版本**: - 确保使用的 C++/WinRT 版本是最新的,并且 Windows SDK 兼容。 - 如果 T-Con 工具依赖于第三方库或框架,确保这些组件也已更新至最新版本,以避免潜在的兼容性问题。 5. **检查串口通信逻辑**: - 如果异常发生在串口通信过程中,检查是否因缓冲区溢出、无效句柄或超时导致 WinRT 层抛出异常。 - 确保串口读写操作不会阻塞主线程,避免因线程同步问题引发异常。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值