Перечислять методы объекта COM (IDispatch), используя ATL? - PullRequest
7 голосов
/ 21 января 2010

Используя ATL (VS2008), как я могу перечислить доступные методы, доступные в данном интерфейсе IDispatch (IDispatch*)? Мне нужно найти метод с определенным именем и, когда у меня есть DISPID, вызвать метод (я знаю параметры, которые принимает метод.) В идеале я хотел бы сделать это, используя умные COM-указатели (CComPtr<>) .

Возможно ли это?

Ответы [ 3 ]

17 голосов
/ 21 января 2010

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

  • через библиотеку типов (если есть) для dispinterface.
  • через звонки IDispatch::GetTypeInfo.

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

Однако, если это так, базовое перечисление включает вызов ITypeInfo::GetTypeAttr, чтобы получить TYPEATTR для интерфейса, и просмотр количества реализованных методов (cFuncs ) и переменные (cVars) и их циклические вызовы и вызов ITypeInfo::GetFuncDesc() или ITypeInfo::GetVarDesc(). Конечно, есть гораздо больше деталей, с которыми вам придется иметь дело, как я могу перечислить здесь, но это должно стать хорошей отправной точкой для вашего исследования.

Вот хорошая статья, объясняющая процесс более подробно с кодом в VB.Net.

12 голосов
/ 12 июля 2013

Вот некоторый код, который выполняет перечисление (он вставляет пары [Dispatch ID] - [Method Name] в карту, но это легко изменить).

///
/// \brief Returns a map of [DispId, Method Name] for the passed-in IDispatch object
///
HRESULT COMTools::GetIDispatchMethods(_In_ IDispatch * pDisp,
                                      _Out_ std::map<long, std::wstring> & methodsMap)
{
    HRESULT hr = S_OK;

    CComPtr<IDispatch> spDisp(pDisp);
    if(!spDisp)
        return E_INVALIDARG;

    CComPtr<ITypeInfo> spTypeInfo;
    hr = spDisp->GetTypeInfo(0, 0, &spTypeInfo);
    if(SUCCEEDED(hr) && spTypeInfo)
    {
        TYPEATTR *pTatt = nullptr;
        hr = spTypeInfo->GetTypeAttr(&pTatt);
        if(SUCCEEDED(hr) && pTatt)
        {
            FUNCDESC * fd = nullptr;
            for(int i = 0; i < pTatt->cFuncs; ++i)
            {
                hr = spTypeInfo->GetFuncDesc(i, &fd);
                if(SUCCEEDED(hr) && fd)
                {
                    CComBSTR funcName;
                    spTypeInfo->GetDocumentation(fd->memid, &funcName, nullptr, nullptr, nullptr);
                    if(funcName.Length()>0)
                    {
                        methodsMap[fd->memid] = funcName;
                    }

                    spTypeInfo->ReleaseFuncDesc(fd);
                }
            }

            spTypeInfo->ReleaseTypeAttr(pTatt);
        }
    }

    return hr;

}
7 голосов
/ 21 января 2010

Вы не можете перечислить все доступные методы, если объект не реализует IDispatchEx.

Однако, если вы знаете имя метода, который хотите вызвать, вы можете использовать GetIDsOfNames, чтобы сопоставить имя с правильным DISPID.

HRESULT hr;
CComPtr<IDispatch> dispatch;
DISPID dispid;
WCHAR *member = "YOUR-FUNCTION-NAME-HERE";
DISPPARAMS* dispparams;

// Get your pointer to the IDispatch interface on the object here.  Also setup your params in dispparams.

hr = dispatch->GetIDsOfNames(IID_NULL, &member, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
if (SUCCEEDED(hr)) {
  hr = dispatch->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, dispparams, &varResult, NULL, NULL);
}

Edit: для полноты я подозреваю, что есть способ опроса интерфейса ITypeInfo2 (при условии, что есть библиотека типов для объекта), который вы получаете из IDispatch :: GetTypeInfo для списка методов, но я не сделал Это. Смотрите другой ответ.

...