Шаг 257.
Среда программирования Visual C++.
Ошибки СОМ-компонентов. Структура кода HRESULT

    На этом шаге мы рассмотрим структуру кода HRESULT.

    Код HRESULT состоит из четырех битовых полей, показанных на рисунке 1:


Рис.1. Поля кода HRESULT

    Описание полей приведено в таблице 1:

Таблица 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.




Предыдущий шаг Содержание Следующий шаг