When using C++ to develop Windows programs, we often see the following judgments:
HRESULT hr = ::RegCreateKeyEx(hk, szKeyPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE, NULL, &hk, NULL);
if (SUCCEEDED(hr))
{
In the code, use the SUCCEEDED
macro to determine the return value of the function RegCreateKeyEx()
.
Some programmers think that when RegCreateKeyEx
returns 0
, it means success, and S_OK
is 0
, so they habitually use the SUCCEEDED
macro to make judgments.
Some people use the following method to judge, which looks more rigorous:
HRESULT hr = ::RegCreateKeyEx(hk, szKeyPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE, NULL, &hk, NULL);
if (S_OK == hr)
{
Indeed, the second type is more rigorous, at least it will not cause a big problem, while the first one is completely a big bug, this bug is not a problem under normal circumstances. But once there is a problem, you can't find it.
What's wrong? Let me introduce it below.
First look at the definition of this macro (WinError.h):
// Generic test for success on any status value (non-negative numbers
// indicate success).
#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)
It can be seen from this that it is to convert hr to HRESULT
type, and then make a judgment whether it is greater than 0. It is also stated in the comment: But when the value is a non-negative number, it means success.
In other words, as long as HRESULT
is a value greater than or equal to 0
, it is considered successful.
Let's look at the definition of HRESULT (winnt.h
):
// Component Object Model defines, and macros
#ifndef _HRESULT_DEFINED
#define _HRESULT_DEFINED
typedef LONG HRESULT;
#endif // !_HRESULT_DEFINED
Oh, it turns out that HRESULT
is an integer of Long
type.
In MSDN, you can find more detailed information:
As shown in the figure above, HRESULT is a 4-byte Long
type with a total of 32 bits. in:
The 31st bit is the s
bit, which is the sign bit, because the HRESUlT
format stipulates that all successes are positive integers, and the failure values are negative numbers.
The 30th bit is the r
bit, which is a reserved bit, but when the n bit (28 bit) is not set, it must be 0
; if the n bit is used, it is used with the s
bit to identify the NTSTATUS
value.
The 29th bit is the c
bit, which means Custom
, that is, the custom bit. If it is a Microsoft-defined return value, this bit is 0
; if it is custom, the bit is 1.
The 28th bit is the n bit, which means NTSTATUS
. If the value is 0
, the value of NTSTATUS
can be mapped to a value of HRESULT
.
The 27th bit is the x bit, which is reserved and must be 0
.
The 26th to 16th bits are Facility
, and 11 bits are used to indicate the source of the error, such as
FACILITY_WINDOWS means from the Windows subsystem
The 15th to the 1st are the Code bits, which are used to store the error value.
It can be seen from this that only the last 2 bytes are used to indicate the return value and the others are auxiliary information, which is mainly used for the return value of the COM
function.
Name | Description | Value |
---|---|---|
S_OK | operation successful | 0x00000000 |
S_FALSE | operation is successful but there is a problem | 0x00000001L |
E_ABORT | operation aborted | 0x80004004 |
E_ACCESSDENIED | Access denied | 0x80070005 |
E_FAIL | unknown error | 0x80004005 |
Note: In addition to S_O
K, there is also a S_FALSE
, which also belongs to success.
Therefore, for the convenience of everyone, Microsoft has specially provided the SUCCEEDED
macro and the FAILED
macro to facilitate your judgment.
At this point, everyone understands: The SUCCEEDED
macro is used to determine whether the function in COM
is executed successfully. Failure is a negative number, and success is 0
and a positive number.
In the previous code, we called a Windows API:
:RegCreateKeyEx(hk, szKeyPath, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE, NULL, &hk, NULL);
The declaration of this API is:
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
);
From MSDN, it returns ERROR_SUCCESS
when it succeeds, other values are failures, and other values are error codes similar to GetLastError
. These error codes are Windows Error Code
.
Windows Error Codes
Microsoft defines a large number of Windows Error Codes in WinError.h
. The range of this error code is 0x0000~0xFFFF
, which is 2 bytes, but it is not limited to 2 bytes, and 4 bytes can also be used. save. In the Windows API, this error code is widely used. For example, the above registry API, its return value is this kind of error code.
Another feature of this error code is that Microsoft defines more detailed and readable description information for these error codes, which can be obtained through the FormatMessage
function. In the Chinese environment, the translated Chinese is displayed.
Windows Error Codes are all positive numbers except ERROR_SUCCESS
, that is, you cannot use the SUCCEEDED
macro to judge, because this macro only judges whether it is a non-negative number. For it, all Windows Error Codes are successful.
Win32 error codes | Description |
---|---|
0x00000000 ERROR_SUCCESS | The operation completed successfully. |
0x00000000 ERROR_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. |
So in the previous code, HRESULT
and Windows Error Code
are confused, especially the first type of code. It will also be judged as successful when the registry fails. The second type is because both are 0, which happens to not be. There is a problem, but it is recommended not to mix it up.