Enumerate COM object (IDispatch) methods using ATL

2019-01-14 19:17发布

问题:

Using ATL (VS2008) how can I enumerate the available methods available on a given IDispatch interface (IDispatch*)? I need to search for a method with a specific name and, once I have the DISPID, invoke the method (I know the parameters the method takes.) Ideally I would like to do this using smart COM pointers (CComPtr<>).

Is this possible?

回答1:

You can't enumerate all the available methods unless the object implements IDispatchEx.

However, if you know the name of the method you want to call, you can use GetIDsOfNames to map the name to the proper 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: For completeness, I suspect there is a way to interrogate the ITypeInfo2 interface (assuming there is a type library for the object) that you get from IDispatch::GetTypeInfo for a list of methods, but I've not done it. See the other answer.



回答2:

You can enumerate the methods an IDispatch exposes through the type info. There are two ways to get the type info:

  • through the type library (if any) for the dispinterface.
  • through calling IDispatch::GetTypeInfo.

Unfortunately, an IDispatch implementation is not obligated to provide type info about the methods and properties it implements.

If it does, however, the basic enumerating involves calling ITypeInfo::GetTypeAttr to get the TYPEATTR for the interface and looking at the number of implemented methods (cFuncs) and variables (cVars) and looping over these and calling ITypeInfo::GetFuncDesc() or ITypeInfo::GetVarDesc(). Of course, there are lot more details you will have to deal with as I can list here, but this should be a good starting point for your exploration.

Here's a nice article explaining the process in more details with code in VB.Net.



回答3:

Here's some code that does the enumeration (it inserts the [Dispatch ID]-[Method Name] pairs in a map, but that's easy to change).

///
/// \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;

}


标签: c++ com atl