Перечислить методы COM-объекта (IDispatch) с помощью ATL?

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

Это возможно?


person Rob    schedule 21.01.2010    source источник
comment
см. этот инструмент (исходный код): sourceforge.net/projects/axfuzz/files   -  person lsalamon    schedule 06.02.2010
comment
и это: codeproject.com/KB/atl/ienum.aspx   -  person lsalamon    schedule 07.02.2010
comment
Я пошел искать другие примеры и также нашел spec.winprog.org/typeinf2   -  person Greg Domjan    schedule 09.06.2010


Ответы (3)


Вы не можете перечислить все доступные методы, если объект не реализует 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);
}

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

person i_am_jorf    schedule 21.01.2010
comment
Блестяще! Как раз то, что мне нужно. Большое спасибо. - person Rob; 21.01.2010
comment
Я считаю, что в своем ответе я сделал все, что вы только что указали в своем комментарии. Пожалуйста, прочтите его еще раз внимательно. Кроме того, автор просто хотел иметь возможность вызывать метод, название которого ему уже было известно. Итак, мой ответ дал решение того, чем он действительно хотел заниматься, а не обязательно то, о чем он просил. Поэтому, подозреваю, он был отмечен как правильный ответ. И, наконец, успокойся, пожалуйста. Это только единицы и нули. - person i_am_jorf; 05.05.2014

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

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

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

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

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

person Franci Penov    schedule 21.01.2010
comment
Хорошая вещь. Спасибо, что добавили это. - person i_am_jorf; 21.01.2010
comment
@Franci, когда свойство является массивом, возвращаемый VARDESC имеет varkind = IDispatch. Как узнать, является ли свойство массивом, а если массив - как мне получить доступ к его членам? При вызове Invoke для получения массива результатом является IDispatch. Этот IDispatch не поддерживает «Элемент», «Длина» или любые подобные свойства. - person Uri; 16.02.2012
comment
@Uri - обратите внимание, что свойства не являются полями и должны проверяться через GetFuncDesc(), что дает вам FUNCDESC, откуда вам нужно перейти к elemdescFunc (для возврата) или lprgelemdescParam (для параметров). Массивы обычно возвращаются как выходной параметр, поэтому вам следует проверить последний. В любом случае, оба они дают ELEMDESC, где вы должны проверить tdesk, который возвращает вам TYPEDESC, который на основе VARTYPE vt может оказаться на самом деле ARRAYDESC. В таком случае у вас SAFEARRAY. - person Franci Penov; 17.02.2012
comment
@Uri - если выясняется, что это не SAFEARRAY, свойство, скорее всего, возвращает указатель IDispatch на объект, который реализует функции, подобные массиву, в виде коллекции. Вам следует запросить у этого IDispatch информацию о его типе и проверить его, чтобы определить правильные методы доступа к членам коллекции. - person Franci Penov; 17.02.2012
comment
@Uri - конечно, все это основано на знаниях, которые я не вытирал около двух лет, поэтому я могу забыть подробности ... :-) - person Franci Penov; 17.02.2012
comment
@Uri - если ваш массив исходит из JScript, это не SAFEARRAY, а IDIspatch для объекта JScript Array. Вот описание разницы между массивами VBScript (которые являются SAFEARRAYS) и массивами JScript - blogs.msdn.com/b/ericlippert/archive/2003/09/22/53061.aspx. - person Franci Penov; 21.02.2012

Вот код, который выполняет перечисление (он вставляет пары [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;

}
person Yiannis Spyridakis    schedule 12.07.2013