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

194

После загрузки библиотеку можно использовать. LoadTypeLib и другие функции возвращают указатель на интерфейс ItypeLib, который используется для доступа к библиотеке типа. Обычно от библиотеки типа Вам требуется информация об интерфейсе или компоненте. Чтобы ее получить, функции ITypeLib::GetTypeInfo передается CLSID или IID, и она возвращает указатель на ItypeInfo для запрошенного элемента.

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

Наиболее часто эти интерфейсы используются инструментами просмотра библиотек типа, т.е. программами, показывающими программисту содержимое библиотеки. Такой инструмент имеется в Visual Basic, он называется Object Browser. С его помощью Вы можете найти конкретный метод данного интерфейса и получить о нем справку. Программа OleView — тоже средство просмотра библиотеки типа. Одна из замечательных возможностей OleView — способность создавать по информации библиотеки типа файл, похожий на файл IDL/ODL. Эта возможность очень полезна.

Библиотеки типа в Реестре

Мне по-настоящему нравятся компоненты, которые сами регистрируют себя в Реестре. Я испытываю огромное отвращение к написанию кода, помещающего данные в Реестр. К счастью, библиотеки типа регистрируются сами. Любопытным может быть интересно, какую именно информацию помещает в Реестр библиотека типа. Запустите REGEDIT.EXE и откройте раздел HKEY_CLASSES_ROOT\TypeLib. Здесь Вы увидите множество LIBID, которые представляют собой GUID, идентифицирующие библиотеки типа. Откройте один из таких GUID и Вы найдете информацию, похожую на ту, что представлена на рис. 11-5.

HKEY_CLASSES_ROOT

TypeLib

{D3011EE1-B997-11CF-A6BB-0080C7B2D682}

Версия

 

1.0

Inside COM Chapter 11 1.0

 

Type Library

 

 

 

0

 

Код языка

Win32

C:\CHAP11\SERVER.TLB

 

FLAGS

0

HELPDIR C:\CHAP11

Рис. 11-5 Информация, добавляемая в Реестр библиотекой типа

Но одну вещь библиотеки типа не регистрируют: Вашему компоненту нужен в Реестре указатель на информацию его библиотеки типа. Поэтому Вы должны добавить раздел с именем TypeLib с GUID библиотеки в раздел CLSID Вашего компонента. Например, следующий раздел должен содержать указанный LIBID:

HKEY_CLASSES_ROOT\ CLSID\

{0C092C29-882C-11CF-A6BB-0080C7B2D682}\ TypeLib

Библиотека типа создает раздел TypeLib для описанных в ней интерфейсов.

Как мы увидим в следующем разделе, библиотеки типа очень важны для реализации IDispatch.

Реализация IDispatch

Вероятно, способов реализации IDispatch не меньше, чем способов ободрать кошку. MFC сторит собственную таблицу имен и указателей на функции. Но реализация дуальных интерфейсов в MFC далека от элегантности. Я покажу Вам самый простой и популярный метод реализации IDispatch. В его основе лежит делегирование вызовов GetIDsOfNames и Invoke методам интерфейса ITypeInfo.

195

Я уже продемонстрировал Вам, как можно получить указатель ITypeInfo для интерфейса. Просто загрузите библиотеку типа и вызовите ITypeLib::GetTypeInfoOfGuid, передавая ей IID интерфейса. GetTypeInfoOfGuid возвращает указатель интерфейса ITypeInfo, который можно использовать для реализации IDispatch. Приведенный ниже код демонстрирует реализацию IDispatch (файл CMPNT.CPP из примера этой главы):

HRESULT __stdcall CA::GetTypeInfoCount(UINT* pCountTypeInfo)

{

*pCountTypeInfo = 1; return S_OK;

}

HRESULT __stdcall CA::GetTypeInfo(

UINT iTypeInfo,

LCID, // Этот объект не поддерживает локализацию

ITypeInfo** ppITypeInfo)

{

*ppITypeInfo = NULL;

if(iTypeInfo != 0)

{

return DISP_E_BADINDEX ;

}

// Вызвать AddRef и вернуть указатель m_pITypeInfo->AddRef();

*ppITypeInfo = m_pITypeInfo; return S_OK;

}

HRESULT __stdcall CA::GetIDsOfNames( const IID& iid,

OLECHAR** arrayNames,

UINT countNames,

LCID, // Локализация не поддерживается

DISPID* arrayDispIDs)

{

if (iid != IID_NULL)

{

return DISP_E_UNKNOWNINTERFACE;

}

HRESULT hr = m_pITypeInfo->GetIDsOfNames(arrayNames, countNames, arrayDispIDs);

return hr;

}

HRESULT __stdcall CA::Invoke( DISPID dispidMember,

const IID& iid,

LCID, // Локализация не поддерживается

WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pvarResult, EXCEPINFO* pExcepInfo, UINT* pArgErr)

{

if (iid != IID_NULL)

{

return DISP_E_UNKNOWNINTERFACE;

}

::SetErrorInfo(0, NULL);

HRESULT hr = m_pITypeInfo->Invoke( static_cast<IDispatch*>(this), dispidMember, wFlags, pDispParams, pvarResult, pExcepInfo, pArgErr);

return hr;

}

196

Восхитительно просто, не так ли? У этого метода есть свои ограничения: например, он не поддерживает интернационализацию. К счастью, большинству компонентов это не нужно. Если Ваш компонент не таков, Вы можете загружать разные библиотеки типа на основании LCID, переданного в вызове Invoke.

Генерация исключений

Как упоминалось в разделе, посвященном параметрам IDispatch::Invoke, предпоследний параметр — структура EXCEPINFO. Чтобы заставить ITypeInfo::Invoke заполнить ее, Вы должны проделать следующую последовательность действий:

1. Реализуйте для своего компонента интерфейс ISupportErrorInfo с единственной функцией-членом:

// ISupportErrorInfo

virtual HRESULT __stdcall InterfaceSupportsErrorInfo(const IID& iid)

{

return (iid == IID_IX) ? S_OK : S_FALSE;

}

2.В своей реализации IDispatch::Invoke вызовите SetErrorInfo(0, NULL) перед вызовом ITypeInfo::Invoke.

3.При возникновении исключительной ситуации вызовите CreateErrorInfo, чтобы получить указатель на интерфейс ICreateErrorInfo.

4.С помощью этого интерфейса предоставьте информацию об ошибке.

5.Наконец, получите указатель на интерфейс IErrorInfo и вызовите SetErrorInfo, передав ей в качестве второго параметра полученный указатель. Первый параметр зарезервирован и всегда равен 0. Все остальное — дело ITypeInfo и клиента.

Ниже приведен пример генерации исключения. (Код взят из функции CA::FxFakeError, которая находится в файле CMPNT.CPP из примера гл. 11.)

//Создать объект «Информация об ошибке»

ICreateErrorInfo* pICreateErr;

HRESULT hr = ::CreateErrorInfo(&pICreateErr); if (FAILED(hr))

{

return E_FAIL;

}

//pICreateErr->SetHelpFile(...);

//pICreateErr->SetHelpContext(...); pICreateErr->SetSource(L"InsideCOM.Chap11");

pICreateErr->SetDescription(

L"Это фиктивная ошибка, сгенерированная компонентом");

IErrorInfo* pIErrorInfo = NULL;

hr = pICreateErr->QueryInterface(IID_IErrorInfo, (void**)&pIErrorInfo);

if (SUCCEEDED(hr))

{

::SetErrorInfo(0L, pIErrorInfo); pIErrorInfo->Release();

}

pICreateErr->Release(); return E_FAIL;

Маршалинг

Если Вы посмотрите на make-файл из примера гл. 11, то увидите, что я не создаю DLL заместителя/заглушки. Это связано с тем, что система, а именно OLEAUT32.DLL, автоматически реализует маршалинг интерфейсов, совместимых с Автоматизацией1. Интерфейс, совместимый с Автоматизацией, наследует IDispatch и использует только такие типы параметров, которые можно поместить в VARIANT. Для таких типов OLEAUT32.DLL выполняет маршалинг автоматически.

Чтобы понять, как это работает, рассмотрим информацию в Реестре для версии интерфейса IX этой главы:

1 Я должен отметить, что в данном случае наш старый метод создания DLL заместителя/заглушки с помощью кода, сгенерированного MIDL, не работает, по крайней мере, для Windows 95 и Windows NT до версии 4.0. Компилятор MIDL не в состоянии сгенерировать код маршалинга VARIANT и BSTR, который будет работать на этих системах. Поэтому, если Вы не хотите использовать OLEAUT32.DLL, Вам придется написать код маршалинга самим.

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