На этом шаге мы рассмотрим структуру кода HRESULT.
Код HRESULT состоит из четырех битовых полей, показанных на рисунке 1:
Рис.1. Поля кода HRESULT
Описание полей приведено в таблице 1:
Идентификатор поля | Биты | Описание |
---|---|---|
S | 31 | Код состояния. 0 - успешное выполнение, 1 - ошибка. Так как этот бит является знаковым, то значение 1 делает код HRESULT отрицательным. |
R | 27-30 | Зарезервированы для кода причины. Должны быть нулевыми. |
Facility (причина) | 16-26 | Код причины, показывающий категорию кода ошибки. |
Code (код) | 0-15 | 16-битное слово, описывающее причину ошибки. |
Коды причины включены в спецификацию COM. Коды ошибок основных категорий (например, FACILITY_NULL, FACILITY_RPC) определены в COM и являются универсальными. Также в COM есть категория FACILITY_ITF для кодов ошибок, определяемых разработчиком. Их назначение зависит от того, какими функциями-членами COM-интерфейса они возвращаются.
При определении собственных кодов HRESULT FACILITY_ITF применяется следующим образом:
#define MY_SUCCESS_CODE ((0 << 31) | (FACILITY_ITF << 16) | 0x200) #define E_MY_ERROR_CODE ((1 << 31) | (FACILITY_ITF << 16) | 0x201)
В файле WinError.h код FACILITY_ITF имеет значение 4, значит в первом примере будет получено значение 0x00040200, а во втором - отрицательное значение 0x80040201. В этом же файле определен макрос MAKE_HRESULT, облегчающий формирование HRESULT "на лету":
//Установка значение HRESULT в 0x80040202.
HRESULT hr = MAKE_RESULT(SEVERITY_ERROR, FACILITY_ITF, 0x202);
Следующее, что нужно помнить при работе с кодами HRESULT: нулевое значение определено для успешного выполнения операции, а не для ошибки. Например, константа S_OK, смысл которой ясен из названия, равна нулю. Как показано в таблице 1, в COM все положительные значения свидетельствуют, что работа может быть выполнена частично, но успешно.
Дабы не утруждать разработчиков запоминанием этих правил, в COM определены два макроса - SUCCEEDED и FAILED, которые допустимо использовать для проверки кодов HRESULT. Конечно, работать с ними необязательно, но читать написанный с их применением код намного легче:
HRESULT hr; hr = pUnk->QueryInterface(IID_SomeObject, (PVOID*) &pObj); if (SUCCEEDED(hr)) { //Использование pObj . . . . . pObj->Release(); }
Как видно из определения макроса SUCCEEDED в файле WinError.h, он возвращает значение TRUE для выражений, значение которых больше или равно 0. В противоположность ему макрос FAILED возвращает TRUE для выражений, имеющих отрицательное значение:
#define SUCCEEDED (Status) ((HRESULT)(Status) >= 0) #define FAILED (Status) ((HRESULT)(Status) < 0)
При работе с кодами HRESULT, которые создали не Вы, проверяйте, как Ваша программа их интерпретирует. Как правило, можно полагаться на то, что константа с префиксом E будет отрицательной, но в остальных случаях никаких предположений делать нельзя. Если Вы в чем-либо не уверены, найдите числовое значение константы в соответствующем заголовочном файле.
На следующем шаге мы рассмотрим класс _com_error.