Оценка 265 Оценить ![]() ![]() ![]() ![]() ![]() ![]()
|
| Введение Смотрим MSDN Пробуем Итого Литература | ![]() |
На RSDN есть статья Павла Блудова “Known IUnknown”, в которой он рассуждает о получении информации об интерфейсах COM-объекта и методах COM-интерфейсов. Предложенные им подходы (перебор всех возможных GUID, сканирование реестра, разбор pdb-файлов и т.д.) являются, на мой взгляд,… э… гм… несколько экзотичными. Существуют более удобные стандартные методы.
Для получения информации о COM-объектах существуют специальные утилиты (смотри хотя бы входящий в состав Visual Studio «OLE/COM Object Viewer»), которые и расскажут, и покажут, и IDL напишут. Но иногда необходимо получать эти данные «на лету», во время выполнения программы. Механизмам получения информации об интерфейсах и посвящена эта статья.
Информация о COM-объектах содержится в библиотеке типов (type library) – двоичном эквиваленте описания объектов на языке IDL, при условии, что таковая имеется. Обычно это файл с расширением .tlb. Кроме того, библиотека типов очень часто содержится в ресурсах исполняемого файла или dll, содержащих код COM-сервера (.exe, .dll и .olb).
Для доступа к этим данным используются два интерфейса: ITypeLib и ITypeInfo.
Для получения информации из библиотеки типов используется интерфейс ITypeLib. Он позволяет получить общую информацию о библиотеке типов, а также предоставляет интерфейс ITypeInfo для описания каждого из перечисленных объектов (см. рисунок 1).

Рисунок 1.
Получить интерфейс ITypeLib можно с помощью функций LoadTypeLib и RegisterTypeLib. Кроме того, если кокласс поддерживает интерфейс IProvideClassInfo, то с его помощью также можно получить ITypeLib. Расширенная версия интерфейса IProvideClassInfo – IProvideClassInfo2 позволяет сразу получить GUID интерфейса, используемого в coclass-е по умолчанию.
Вот некоторые функции интерфейса ITypeLib:
UINT GetTypeInfoCount(); |
Этот метод возвращает количество объектов в библиотеке типов.
HRESULT GetTypeInfo( unsignedint index, ITypeInfo ** ppTInfo); |
Метод GetTypeInfo позволяет получить описание элемента с номером index.
HRESULT GetDocumentation( int index, BSTR FAR* pBstrName, BSTR FAR* pBstrDocString, unsignedlong FAR* pdwHelpContext, BSTR FAR* pBstrHelpFile); |
Этот метод позволяет получить для элемента с номером index название (pBstrName), описание (pBstrDocString), файл справки (pBstrHelpFile), индекс в файле справки (pdwHelpContext).
|
Полученные строки необходимо удалить самостоятельно (вызвать SysFreeString). Если один из элементов не нужен, вместо указателя передается NULL |
Существует также расширенная версия этого интерфейса – ITypeLib2. Он позволяет получать информацию об элементах в различных контекстах локализации (функция GetDocumentation2), а также предоставляет некоторые другие возможности.
Для получения информации об объектах, описанных в библиотеке типов (интерфейсах, coclass-ах и т.д.), используется интерфейс ITypeInfo. С его помощью можно получить свойства элементов (название, GUID и т.д.), а также информацию о методах интерфейсов.
Вот некоторые функции этого интерфейса.
HRESULT GetTypeAttr(TYPEATTR FAR* FAR* ppTypeAttr); |
Эта функция позволяет получить структуру TYPEATTR, содержащую GUID элемента (если таковой имеется) – guid; тип элемента – typekind; количество методов в интерфейсе – cFuncs. Структуру не надо создавать заранее, достаточно просто передать NULL. Освободить память, выделенную для этой структуры, можно при помощи функции ReleaseTypeAttr.
HRESULT GetFuncDesc(unsignedint index, FUNCDESC FAR* FAR* ppFuncDesc); |
Эта функция позволяет получить структуру FUNCDESC, описывающую метод с заданным индексом. Эта структура содержит тип функции – funckind; количество параметров – cParams; тип результата, возвращаемого функцией – elemdescFunc; указатель на массив, содержащий типы параметров метода – lprgelemdescParam; идентификатор метода – memid. Структуру не надо создавать заранее, достаточно просто передать NULL.
| ПРИМЕЧАНИЕ Информация о типах параметров и типе возврата фактически хранится в виде поля типа TYPEDESC. Ее поле vt является описанием automation-типа, т.е. может принимать значения: VT_BSTR, VT_I2, VT_DATE и т.д. Пример приведен ниже. |
ReleaseFuncDesc уничтожает созданную с помощью GetFuncDesc структуру.
HRESULT GetDocumentation( MEMBERID memid, BSTR FAR* pBstrName, BSTR FAR* pBstrDocString, unsignedlong FAR* pdwHelpContext, BSTR FAR* pBstrHelpFile); |
Позволяет получить для метода с номером memid (см. описание функции GetFuncDesc) название (pBstrName), описание (pBstrDocString), файл справки (pBstrHelpFile), индекс в файле справки (pdwHelpContext).
| ПРИМЕЧАНИЕ Полученные строки необходимо удалить самостоятельно (вызвать SysFreeString). Если один из элементов не нужен, вместо указателя предается NULL. |
CString DecodeTYPEDESC(ITypeInfo* pti, TYPEDESC* ptdesc)
{
CString str;
// Это указатель?if (ptdesc->vt == VT_PTR)
{
// Тогда проверяем тип указателя
str = DecodeTYPEDESC(pti, ptdesc->lptdesc);
// И добавляем "*"
str += _T("*");
return str.AllocSysString();
}
// Это массив?if ((ptdesc->vt & 0x0FFF) == VT_CARRAY)
{
// Тогда определяем тип его элементов...
str = DecodeTYPEDESC(pti, &ptdesc->lpadesc->tdescElem);
// ...и размерность
CString strTemp;
for (USHORT n = 0; n < ptdesc->lpadesc->cDims; n++)
{
strTemp.Format(_T("[%d]"), ptdesc->lpadesc->
rgbounds[n].cElements);
str += strTemp;
}
return str;
}
// Это SAFEARRAY?if ((ptdesc->vt & 0x0FFF) == VT_SAFEARRAY)
{
str = _T("SAFEARRAY(") + DecodeTYPEDESC(pti, ptdesc->lptdesc);
return str;
}
// Это пользовательский тип (UDT)?...if (ptdesc->vt == VT_USERDEFINED)
{
ASSERT(pti);
ITypeInfo* ptiRefType = NULL;
// ...тогда получаем его название
HRESULT hr = pti->GetRefTypeInfo(ptdesc->hreftype, &ptiRefType);
if (SUCCEEDED(hr))
{
BSTR bstrName = NULL;
BSTR bstrDoc = NULL;
BSTR bstrHelp = NULL;
DWORD dwHelpID;
hr = ptiRefType->GetDocumentation(MEMBERID_NIL, &bstrName,
&bstrDoc, &dwHelpID, &bstrHelp);
if (FAILED(hr))
return _T("ITypeInfo::GetDocumentation() failed");
str = bstrName;
SysFreeString(bstrName);
SysFreeString(bstrDoc);
SysFreeString(bstrHelp);
ptiRefType->Release();
}
elsereturn _T("GetRefTypeInfo failed");
return str;
}
VARTYPE vt = ptdesc->vt & ~0xF000;
if (vt <= VT_CLSID)
// Если это не специальный случай, просто получаем описание типа
DecodeVARTYPE(vt, str);
else
str = _T("BAD VARTYPE");
return str;
}
|
void DecodeVARTYPE(VARTYPE vt, CString &strDesc)
{
switch (vt)
{
case VT_EMPTY: strDesc = _T("void"); break;
case VT_NULL: strDesc = _T("NULL"); break;
case VT_I2: strDesc = _T("short"); break;
case VT_I4: strDesc = _T("long"); break;
case VT_R4: strDesc = _T("single"); break;
case VT_R8: strDesc = _T("double"); break;
case VT_CY: strDesc = _T("CURRENCY"); break;
case VT_DATE: strDesc = _T("DATE"); break;
case VT_BSTR: strDesc = _T("BSTR"); break;
case VT_DISPATCH: strDesc = _T("IDispatch*"); break;
case VT_ERROR: strDesc = _T("SCODE"); break;
case VT_BOOL: strDesc = _T("BOOL"); break;
case VT_VARIANT: strDesc = _T("VARIANT"); break;
case VT_UNKNOWN: strDesc = _T("IUnknown*"); break;
case VT_I1: strDesc = _T("char"); break;
case VT_UI1: strDesc = _T("unsigned char"); break;
case VT_UI2: strDesc = _T("unsigned short"); break;
case VT_UI4: strDesc = _T("unsigned long"); break;
case VT_I8: strDesc = _T("int64"); break;
case VT_UI8: strDesc = _T("uint64"); break;
case VT_INT: strDesc = _T("int"); break;
case VT_UINT: strDesc = _T("unsigned int"); break;
case VT_VOID: strDesc = _T("void"); break;
case VT_HRESULT: strDesc = _T("HRESULT"); break;
case VT_PTR: strDesc = _T("void*"); break;
case VT_SAFEARRAY: strDesc = _T("SAFEARRAY"); break;
case VT_CARRAY: strDesc = _T("CARRAY"); break;
case VT_USERDEFINED: strDesc = _T("USERDEFINED"); break;
case VT_LPSTR: strDesc = _T("LPSTR"); break;
case VT_LPWSTR: strDesc = _T("LPWSTR"); break;
case VT_FILETIME: strDesc = _T("FILETIME"); break;
case VT_BLOB: strDesc = _T("BLOB"); break;
case VT_STREAM: strDesc = _T("STREAM"); break;
case VT_STORAGE: strDesc = _T("STORAGE"); break;
case VT_STREAMED_OBJECT: strDesc = _T("STREAMED_OBJECT"); break;
case VT_STORED_OBJECT: strDesc = _T("STORED_OBJECT"); break;
case VT_BLOB_OBJECT: strDesc = _T("BLOB_OBJECT"); break;
case VT_CF: strDesc = _T("CF"); break;
case VT_CLSID: strDesc = _T("CLSID"); break;
default: strDesc.Format(_T("[Unknown type = %d]"), vt);
}
}
|
Попробуем получить описание всех методов для всех интерфейсов некоторой библиотеки типов. Предполагается, что в переменной strFilePath содержится путь к файлу. Информация о параметрах методов выводится в виде кодов, применяющихся для определения VARTYPE (смотри описание этого типа или констант VT_XXX в MSDN).
ITypeLib *iLib = NULL; CComBSTR oFile(strFilePath); // Пробуем получить информацию о библиотеке типовif (FAILED(LoadTypeLib(oFile, &iLib))) { MessageBox(_T("\" ") + strFilePath + _T(" \"\ncontains no registered type library."), _T("Error"), MB_OK | MB_ICONWARNING); return; } UINT n, m, k, nCount = iLib->GetTypeInfoCount(); ITypeInfo *iInfo = NULL; TYPEATTR *pTypeAttr; FUNCDESC *pFuncInfo; CString strFmt, strNode; BSTR strName; HTREEITEM hNode, hFunction; // Для каждого объектаfor (n = 0; n < nCount; n++) { // Получаем информацию об объектеif (FAILED(iLib->GetTypeInfo(n, &iInfo))) continue; if (FAILED(iInfo->GetTypeAttr(&pTypeAttr))) { iInfo->Release(); continue; } // Получаем имя объекта iLib->GetDocumentation(n, &strName, NULL, NULL, NULL); strNode = CString(strName); SysFreeString(strName); // Получаем GUID объекта CComBSTR strGuid(pTypeAttr->guid); strGuid.ToUpper(); strNode += _T(" ") + CString(strGuid); // Определяем тип объектаswitch (pTypeAttr->typekind) { case TKIND_ENUM: strFmt = _T("Enumeration"); break; case TKIND_RECORD: strFmt = _T("Record"); break; case TKIND_MODULE: strFmt = _T("Module"); break; case TKIND_INTERFACE: strFmt = _T("Interface"); break; case TKIND_DISPATCH: strFmt = _T("Dispatch interface"); break; case TKIND_COCLASS: strFmt = _T("Coclass"); break; case TKIND_ALIAS: strFmt = _T("Alias"); break; case TKIND_UNION: strFmt = _T("Union"); break; default: strFmt = _T("*** Unknown type ***"); } strNode = strFmt + _T(" ") + strNode; // Выводим информацию об объекте cout << strNode << endl; // Если это интерфейсif (pTypeAttr->typekind == TKIND_INTERFACE || pTypeAttr->typekind == TKIND_DISPATCH) { // То для каждого методаfor (m = 0; m < (UINT) pTypeAttr->cFuncs; m++) { // Получаем информацию о методеif (FAILED(iInfo->GetFuncDesc(m, &pFuncInfo))) break; // Получаем название метода iInfo->GetDocumentation(pFuncInfo->memid, &strName, NULL, NULL, NULL); strNode = CString(strName); SysFreeString(strName); // Определяем тип методаswitch (pFuncInfo->invkind) { case INVOKE_FUNC: strFmt = _T("Function"); break; case INVOKE_PROPERTYGET: strFmt = _T("Property access"); break; case INVOKE_PROPERTYPUT: strFmt = _T("Property assign"); break; case INVOKE_PROPERTYPUTREF: strFmt = _T("Property assign by reference"); break; default: strFmt = _T("*** Unknown function type ***"); } strNode = _T("\t") + strFmt + _T(": \" ") + strNode + _T(" \""); // Выводим информацию о методе cout << strNode << endl; // Определяем тип возврата метода strNode.Format(_T("\t\tReturn type: %d"), pFuncInfo->elemdescFunc.tdesc.vt); // Выводим тип возврата cout << strNode << endl; // Для каждого параметра метода...for (k = 0; k < (UINT) pFuncInfo->cParams; k++) { // ...получаем информацию о параметре strNode.Format(_T("\t\tParameter %d type: %d"), k + 1, pFuncInfo->lprgelemdescParam[k].tdesc.vt); // ...выводим информацию о параметре cout << strNode << endl; } iInfo->ReleaseFuncDesc(pFuncInfo); } } iInfo->ReleaseTypeAttr(pTypeAttr); iInfo->Release(); } iLib->Release(); |
Если немного улучшить предложенный код (смотри демонстрационный проект), можно получить полноценную информацию о методах COM-интерфейсов (на рисунке 2 приведен пример для интерфейсов Microsoft Access):
Рисунок 2.
Информация, получаемая из библиотеки типов, не является исчерпывающей. COM-объект может вообще не иметь библиотеки типов. Кроме того, в библиотеке типов может быть описана только часть реализуемых COM-объектом интерфейсов. Так, большинство элементов управления ActiveX описывает в библиотеке типов только так называемые пользовательские интерфейсы (интерфейсы, которые в основном и использует прикладной программист). В то же время о множестве интерфейсов, реализованных в этом элементе управления, никакой информации в библиотеке типов не содержится. Так что статья Павла Блудова “Known IUnknown” не теряет своей актуальности.
Но при наличии библиотек типов описанный способ - самый простой, быстрый и точный.
Оценка 265 Оценить ![]() ![]() ![]() ![]() ![]() ![]()
|