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?
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.
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.
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;
}