Тип данных HRESULT

Аватар пользователя @mabu опубликовал

HRESULT содержит информацию о результате вызова функции. Хотя из названия HRESULT можно было бы заключить, что это описатель (Handle) результата, на самом деле это не так. Название возникло по историческим причинам, просто расшифровывай его как «вот результат» (here is the result). HRESULT похож на код ошибки Windows, но это не одно и то же, и смешивать их не следует.

Внутреннее устройство HRESULT

В заголовочном файле winnt.bi (входит в windows.bi) тип данных HRESULT описан как 32‐битное знаковое целое число:

Type HRESULT As LONG

Детальный разбор

Биты числа 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Описание Severity R C N r Facility Код возврата
Бит 31:
Признак критичности: 1 если произошла ошибка, 0 при успешном результате.
Биты с 30 по 27:
Зарезервированы.
Биты с 26 по 16:
Идентификатор средства: какая часть операционной системы выдаёт данный код возврата.
Биты с 15 по 0:
Код ошибки или причина успеха. Определяется корпорацией Microsoft или пользователем.

Признак критичности

Бит 31 (Severity) — это признак критичности. Позволяет использовать HRESULT для индикации как успешного выполнения функций, так и при ошибке.

Константа критичности Значение Описание
SEVERITY_SUCCESS 0 Функция отработала успешно
SEVERITY_ERROR 1 Функция завершилась ошибкой

Идентификатор средства

Пятнадцать битов — с 30-го по 16-й — содержат идентификатор средства (Facility). Он указывает, какая часть операционной системы выдаёт данный код возврата. Поскольку операционную систему разрабатывает корпорация Microsoft, она зарезервировала право определения идентификаторов средств за собой.

Константа идентификатора средства Значение Источник ошибки
FACILITY_NULL 0 Стандартные коды возврата
FACILITY_RPC 1 Удалённый вызов процедур RPC
FACILITY_DISPATCH 2 Объект автоматизации
FACILITY_STORAGE 3 Хранилище OLE
FACILITY_ITF 4 COM или OLE интерфейс сторонних разработчиков
FACILITY_WIN32 7 Декорированная ошибка функций Windows
FACILITY_WINDOWS 8 Подсистема Windows
FACILITY_SECURITY 9 Уровень безопасности

В том случае, если стандартных значений недостаточно, то при создании собственных HRESULT следует устанавливать идентификатор средства в FACILITY_ITF.

Смысл кода возврата, отмеченного с помощью FACILITY_ITF, специфичен для возвращающих его функций и является уникальным только в пределах данной функции. Одно и то же значение кода возврата в FACILITY_ITF, возвращаемое двумя различными функциями, может иметь разные значения.

Иногда функция вызывает другие функции, возвращающие HRESULT и отмеченные FACILITY_ITF. В этом случае такие коды возврата следует преобразовывать в специфичные для твоей функции; не следует отдавать их «как есть».

Все COM‐определённые коды FACILITY_ITF имеют значение кода в диапазоне &h0000-&h01FF. Хотя использование любых кодов в FACILITY_ITF является законным, рекомендуется использовать только значения кода в диапазоне &h0200-&hFFFF для уменьшения путаницы с любыми COM‐определёнными ошибками.

Некоторые предопределённые HRESULT

В таблице приведены часто используемые константы HRESULT. По соглашению в названиях успешных кодов содержится S_, а в названиях кодов ошибок E_.

Константа Значение Описание
S_OK &h00000000 Функция отработала успешно. В некоторых случаях этот код также означает логическую истину.
S_FALSE &h00000001 Функция отработала успешно и возвращает логическую ложь. Это несколько противоречит обычной практике программирования, где 0 — это ложь, а не-0 — истина.
E_PENDING &h8000000A Данные, необходимые для завершения операции, пока отсутствуют.
E_NOTIMPL &h80004001 Метод не реализован.
E_NOINTERFACE &h80004002 Запрашиваемый интерфейс не поддерживается.
E_POINTER &h80004003 Указатель недействителен.
E_ABORT &h80004004 Операция прервана.
E_FAIL &h80004005 Ошибка по неуказанной причине.
E_UNEXPECTED &h8000FFFF Произошёл катастрофический сбой.
E_ACCESSDENIED &h80070005 Доступ запрещён.
E_HANDLE &h80070006 Описатель недействителен.
E_OUTOFMEMORY &h8007000E Невозможно выделить память.
E_INVALIDARG &h80070057 Один или несколько аргументов неправильные.

Большой и длинный список HRESULT можно найти в статье на MSDN «HRESULT Values».

Использование HRESULT

Макросы SUCCEEDED и FAILED

Для проверки успешного или ошибочного результата функции нельзя сравнивать HRESULT с каким‐либо одним кодом. Например, если функция завершилась успешно, то нельзя ожидать возврата только S_OK, и наоборот — при ошибке нелья ждать только E_FAIL. Следует использовать предопределённые макросы SUCCEEDED и FAILED.

' Вызываем функцию и получаем HRESULT:
Dim hr As HRESULT = SomeFunction(param)

' Не делай так:
If hr = E_FAIL Then
    Print "Ошибка"
End If
If hr = S_OK Then
    Print "Успешно"
End If

' Следует делать так:

' Проверка на ошибку:
If FAILED(hr) Then
    Print "Ошибка"
End If

' Проверка на успех:
If SUCCEEDED(hr) Then
    Print "Успешно"
End If

Макрос HRESULT_CODE

Получает код возврата.

Dim hr As HRESULT = SomeFunction(param)
Dim Code As Integer = HRESULT_CODE(hr)

Макрос HRESULT_FACILITY

Получает идентификатор средства.

Dim hr As HRESULT = SomeFunction(param)
Dim Facility As Integer = HRESULT_FACILITY(hr)

Макрос HRESULT_SEVERITY

Получает признак критичности.

Dim hr As HRESULT = SomeFunction(param)
Dim Severity As Integer = HRESULT_SEVERITY(hr)

Макрос MAKE_HRESULT

Создаёт собственный HRESULT. Все идентификаторы средств зарезервировала за собой корпорация Microsoft, поэтому стороннему разработчику следует использовать FACILITY_ITF, а код возврата начинать с &h0200.

' Создаём собственный HRESULT с ошибкой:
Const CUSTOM_E_PROTOCOLPERMISSION As HRESULT = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, &h0201)

' Создаём собственный HRESULT с успехом:
Const CUSTOM_S_PROTOCOLACCESS As HRESULT = MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_ITF, &h0201)

При именовании констант собственных HRESULT хорошей практикой является приписывание слева имени компонента или приложения. Например:

AIRPLANE_E_LANDINGWITHGEARUP
HELICOPTER_S_ROTORRPMGREEN

Макрос HRESULT_FROM_WIN32

Создаёт HRESULT из ошибки Windows. В этом случае признак критичности автоматически принимает значение SEVERITY_ERROR, а идентификатор средства — FACILITY_WIN32.

' Получаем код ошибки Windows:
Dim dwError As DWORD = GetLastError()

' Упаковываем его в HRESULT:
Dim hr As HRESULT = HRESULT_FROM_WIN32(dwError)