Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Роджерсон Д. - Основы COM - 2000.pdf
Скачиваний:
411
Добавлен:
13.08.2013
Размер:
2.4 Mб
Скачать

6 глава

HRESULT, GUID,

Реестр и другие детали

Дух братьев Райт все еще жив. Каждый год сотни людей в своих гаражах строят самолеты из наборов «Сделай сам». Они делают не пластиковые игрушки, радиоуправляемые модели или легковесные матерчатые конструкции. Они строят современные двухместные самолеты с полностью закрытой кабиной из современнейших композитных материалов. По правилам FAA* достаточно, чтобы производитель набора выполнил только 49% всей работы по постройке самолета. Оставшийся 51% конструктор-любитель делает сам.

Постройка 51% самолета занимает, в зависимости от модели, примерно от 250 до 5000 часов. Большинство производителей предлагают наборы «для быстрого приготовления», в которых многие части уже собраны, например, сварены рамы и пропитаны детали из композитных материалов. Используя такие заготовки, можно быстро сделать нечто, похожее на самолет. Однако это будет еще далеко не настоящий самолет. Куча времени уходит на разные мелочи — установку панели управления и приборов, сидений, ремней безопасности, огнетушителей, покрытия, сигнализации, табличек, кабелей управления, электропроводки, лампочек, батарей, брандмауэров, вентиляционных люков, крыши пилотской кабины, отопителя, окон, замков и ручек на дверях и еще многого другого.

Многие энтузиасты самолетостроения приходят в уныние, разочаровываются и даже бросают это занятие, потратив на сборку многие часы. Точно так же многие начинают изучать сложный предмет, например СОМ, лишь затем, чтобы захлебнуться в деталях и все бросить. В первых пяти главах книги я пытался максимально избегать подробностей, чтобы Вы сосредоточились на общей картине. В этой главе я собираюсь обсудить некоторые их тех деталей, которые раньше пропускал или скрывал. Я хочу рассмотреть и другие детали, которые понадобятся нам в следующих главах.

Сначала мы обсудим HRESULT — тему, впервые возникшую в гл. 3 в связи с QueryInterface. Затем мы рассмотрим GUID. Один из примеров GUID — структура IID, передаваемая QueryInterface. После обсуждения этих типов мы познакомимся с тем, как компоненты публикуют в Реестре данные о своем местонахождении (это позволяет клиентам находить и создавать компоненты). В заключение мы рассмотрим некоторые полезные функции и утилиты библиотеки СОМ.

HRESULT

Во всех самолетах есть приборы, и самодельные самолеты — не исключение. Хотя в некоторых таких самолетах роль приборов играет компьютер с цветным графическим дисплеем (иногда даже «под» Windows NT), обычно ставят что-нибудь подешевле. Металлическая полоса, например, — простейший индикатор скорости. Чем быстрее Вы летите, тем сильнее она изгибается.

Хотя приборы и могут сообщить в деталях, что происходит с самолетом и отдельными системами, их основное назначение — предупреждать пилота об опасности. На индикаторе скорости есть красная полоска, отмечающая слишком высокую (или низкую) скорость. Часто приборы снабжаются аварийными лампочками и зуммерами.

У компонентом СОМ нет приборов. Вместо шкал или лампочек для сообщений о текущем состоянии дел они используют HRESULT. QueryInterface возвращает HRESULT. И, как мы увидим в оставшейся части книги, большинство функций интерфейсов СОМ также возвращает HRESULT. Хотя из названия HRESULT можно было бы заключить, что это описатель (handle) результата, на самом деле это не так. HRESULT — это 32-разрядное значение, разделенное на три поля. Значение полей, составляющих HRESULT, поясняет рис. 6-1. Название возникло по историческим причинам; просто расшифровывайте его как «вот результат» (here’s the result), а не

«описатель результата» (handle of result).

* Federal Aviation Agency, Федеральное авиационное агентство США. — Прим. перев.

76

Определенные системой значения HRESULT содержатся в заголовочном файле Win32 WINERROR.H. В начале файла расположены коды ошибок Win32, так что его нужно пролистать, чтобы добраться до HRESULT. HRESULT похож на код ошибки Win32, но это не одно и то же, и смешивать их не следует.

Старший бит HRESULT, как показано на рис. 6-1, отмечает, успешно или нет выполнена функция. Это позволяет определить много кодов возврата и для успеха, и для неудачи. Последние 16 битов содержат собственно код возврата. Остальные 15 битов содержат дополнительную информацию о типе и источнике ошибки.

Признак

критичности

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

15 битов

 

 

 

 

 

16 битов

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Средство

 

Код возврата

 

 

 

 

 

31 30

16

15

0

Рис. 6-1 Формат HRESULT

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

Таблица 6-1 Распространенные значения HRESULT

Название

Значение

 

 

S_OK

Функция отработала успешно. В некоторых случаях этот код

 

также означает, что функция возвращает логическую истину.

 

Значение S_OK равно 0

NOERROR

То же, что S_OK

S_FALSE

Функция отработала успешно и возвращает логическую ложь.

 

Значение S_FALSE равно 1

E_UNEXPECTED

Неожиданная ошибка

E_NOIMPL

Метод не реализован

E_NOINTERFACE

Компонент не поддерживает запрашиваемый интерфейс.

 

Возвращается QueryInterface

E_OUTOFMEMORY

Компонент не может выделить требуемый объем памяти

E_FAIL

Ошибка по неуказанной причине

 

 

Обратите внимание, что значение S_FALSE равно 1, а значение S_OK — 0. Это противоречит обычной практике программирования на С/С++, где 0 — это ложь, а не-0 — истина. Поэтому при использовании HRESULT обязательно явно сравнивайте коды возврата с S_FALSE или S_OK.

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

Таблица 6-2 Идентификаторы средств, определенные в настоящее время

FACILITY_WINDOWS

8

FACILITY_STORAGE

3

FACILITY_SSPI

9

FACILITY_RPC

1

FACILITY_Win32

7

FACILITY_CONTROL

10

FACILITY_NULL

0

FACILITY_ITF

4

FACILITY_DISPATCH

2

FACILITY_CERT

11

77

Идентификатор средства освобождает, например, разработчиков Microsoft, занятых RPC (FACILITY_RPC), от необходимости согласовывать значения кодов возврата с теми, кто работает над управляющими элементами ActiveX (FACILITY_CONTROL). Поскольку группы разработчиков используют разные идентификаторы средств, коды возврата разных средств не будут конфликтовать. Разработчикам специализированных интерфейсов повезло меньше.

Все идентификаторы средств, кроме FACILITY_ITF, задают определенные СОМ универсальные коды возврата. Эти коды всегда и везде одни и те же. FACILITY_ITF — исключение; ему отвечают коды, специфичные для данного интерфейса. Чтобы определить средство для данного HRESULT, используйте макрос HRESULT_FACILITY, определенный в WINERROR.H. Как Вы увидите в разделе «Определение собственных кодов возврата», коды FACILITY_ITF не уникальны и могут иметь разные значения в зависимости от интерфейса, возвратившего код. Но прежде чем определять собственные коды, давайте рассмотрим использование HRESULT.

Поиск HRESULT

Как уже отмечалось, определение всех кодов состояния СОМ (и OLE — точнее, уже ActiveX), генерируемых системой в настоящее время, содержится в WINERROR.H. Обычно коды заданы как шестнадцатеричные числа; запись для E_NOINTERFACE выглядит так:

//MessageID: E_NOINTERFACE

//MessageText:

//

// Данный интерфейс не поддерживается*

//

 

#define E_NOINTERFACE

0x80004002L

Однако если идентификатор средства HRESULT равен FACILITY_WIN32, Вы можете не найти его среди других. Часто это будет код ошибки Win32, преобразованный в HRESULT. Чтобы найти его значение, отыщите код ошибки Win32, совпадающий с последними 16 битами. Пусть, например, интерфейс возвращает код ошибки 0x80070103. Число 7 в середине — это идентификатор средства FACILITY_WIN32. В файле WINERROR.H Вы не найдете этот код там, где перечислены другие HRESULT. Поэтому переведите последние 16 битов из шестнадцатеричного представления в двоичное; получится число 259, которое уже можно найти в списке кодов

Win32.

//MessageID: ERROR_NO_MORE_ITEMS

//MessageText:

//

// Больше элементов нет

//

 

#define ERROR_NO_MORE_ITEMS

259L

Искать HRESULT в WINERROR.H вполне допустимо, когда мы пишем код. Однако нашим программам необходим способ получить сообщение об ошибке, соответствующее данному HRESULT, и отобразить его пользователю. Для отображения сообщений о стандартных ошибках СОМ (а также ActiveX, ранее OLE, и Win32) можно использовать API Win32 FormatMessage:

void ErrorMessage(LPCTSTR str, HRESULT hr)

{

void* pMsgBuf;

::FormatMessage(

FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,

hr,

MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pMsgBuf,

0, NULL

);

// Отобразить строку cout << str << “\r\n”;

cout << “Error (” << hex << hr << “): ” << (LPTSTR)pMsgBuf << endl;

* В WINERROR это сообщение, как Вы могли догадаться, приведено по-английски. — Прим. перев.

78

// Освободить буффер

LocalFree(pMsgBuf);

}

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

Как видите, использовать HRESULT несколько сложнее, чем обычные булевы коды возврата. Среди потенциально чреватых осложнениями особенностей можно назвать:

!" множественность кодов как успешного, так и ошибочного завершения; !" тот факт, что коды ошибок могут изменяться.

Множественность кодов завершения

Как правило, в зависимости от обстоятельств функции возвращают различные коды как успешного, так и ошибочного завершения. Именно поэтому мы использовали SUCCEEDED и FAILED. Для проверки успешного завершения нельзя сравнивать HRESULT с каким-либо одним кодом, например S_OK; равно как и для проверки неудачного завершения HRESULT нельзя сравнивать с каким-то одним кодом, например E_FAIL. Иными словами, нельзя писать:

HRESULT hr = CreateInstance(...);

if (hr == E_FAIL)

// Не делайте так!

return;

 

hr = pI->QueryInterface(...);

if (hr == S_OK)

// Не делайте так!

{

 

pIX->Fx();

 

pIX->Release();

}

pI->Release();

Вместо этого надо использовать макросы SUCCEEDED и FAILED.

HRESULT hr = CreateInstance(...); if (FAILED(hr))

return;

hr = pI->QueryInterface(...); if (SUCCEEDED(hr))

{

pIX->Fx(); pIX->Release();

}

pI->Release();

Коды ошибок могут изменяться

После того, как Ваш клиент закончен, другие разработчики могут определить новые коды ошибок для HRESULT, с которыми столкнется клиент. Поскольку компоненты, используемые клиентом, могут меняться, могут изменяться и возвращаемые ими коды ошибок. Предположим, мы пишем компонент как сервер внутри процесса. Некоторое время спустя мы решили модернизировать его, сделав удаленным сервером на другой машине. Первая версия компонента не возвращала никаких кодов ошибок сети, тогда как вторая версия может это делать. Клиент не может заранее знать обо всех возможных ошибках, поэтому он должен быть готов к обработке неожиданных ошибок. Обрабатывайте все неожиданные ошибки так же, как E_UNEXPECTED.

С кодами успешного окончания этой проблемы нет. Набор этих кодов для Вашей функции должен быть статичным. Коды успешного завершения — часть интерфейса и поэтому не могут изменяться. Клиент, использующий интерфейс, должен быть способен понять, предсказать и обработать все возможные случаи успешного завершения, поскольку ему надо будет продолжать работу. Клиенту нет необходимости обрабатывать все возможные коды ошибок — он не обязан продолжать работу, если ему встретился неожиданный код.

HRESULT и сеть

Часто связь с удаленной машиной по сети неожиданно прерывается. Если клиент работает с удаленным компонентом, он должен уметь элегантно обрабатывать разрыв сетевого соединения. Это означает, что у каждого вызова функции, который может выполняться по сети, должен быть некий способ индикации разрыва связи. По этой причине все методы, которые могут выполняться на удаленной машине, должны возвращать HRESULT. Избегайте других типов возвращаемого значения, например:

Соседние файлы в предмете Программирование на C++