I am writing an Excel RTD server implementation and I'm stuck on the boilerplate for a coclass which implements IDispatch
. I have no access to ATL, but I am using ActiveQt, although I'm interested in how to do this in raw C or C++ too. How to properly implement the IDispatch
methods in a COM server?
The documentation is just panickingly awful, as always. What I have so far read:
IDispatch
method calls to some ITypeInfo
. Is this correct?ITypeInfo
to myself? LoadTypeLib() and family (followed by looking at ITypeLib::GetTypeInfo()
)?The LoadTypeLib()
approach seems appropriate for a COM client to reach type information for some library, not for a COM server trying to introspect itself. Am I correct?
Implementing IDispatch can be easy or hard. (Assuming you cannot use ATL).
The easy way is to not support TypeInfo (return 0 from GetTypeInfoCount
and E_NOTIMPL
from GetTypeInfo
. Nobody should call it.).
Then all you have to support is GetIDsOfNames
and Invoke
. It's just a big lookup table essentially.
For GetIDsOfNames
, return DISP_E_UNKNOWNNAME
if cNames != 1
. You aren't going to support argument names. Then you just have to lookup rgszNames[0]
in your mapping of names-to-ids.
Finally, implement Invoke. Ignore everything except pDispParams and pVarResult. Use VariantChangeType to coerce the parameters to the types you expect, and pass to your implementation. Set the return value and return. Done.
The hard way is to use ITypeInfo and all that. I've never done it and wouldn't. ATL makes it easy so just use ATL.
If picking the hard way, Good luck.
If the interface is properly defined in the IDL and compiled into a type library, implementing IDispatch
via the type library's ITypeInfo
is quite feasible as it's mostly delegating. The interesting part is ITypeInfo::Invoke
which relies upon correct C++ v-table layout:
public class CComClass: public IDualInterface
{
// ...
// implementing IDualInterface
ITypeInfo* m_pTypeInfo // can be obtained via ITypeLib::GetTypeInfoOfGuid for the GUID of IDualInterface
// IDispatch
STDMETHODIMP GetTypeInfoCount(UINT* pctinfo)
{
*pctinfo = 1;
return S_OK;
}
STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
{
if (0 != itinfo)
return E_INVALIDARG;
(*pptinfo = m_pTypeInfo)->AddRef();
return S_OK;
}
STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid)
{
return m_pTypeInfo->GetIDsOfNames(rgszNames, cNames, rgdispid);
}
STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
{
return m_pTypeInfo->Invoke(static_cast<IDualInterface*>(this), dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
}
}
I've used a similar approach to create a script-callable wrapper for MSHTML DOM objects to bypass scripting security restrictions.
So where do you get the ITypeInfo from? Essentially you get it by:
ITypeInfo
implementation knows which function to invoke - it cannot just invoke the C++ functions directly on your class because C++ has no reflection and because it is language neutral. Therefore it can only delegate the Invoke
call to another method declared in the type library.E_NOTIMPL
then implement them one by one)RegisterTypeLib
. If it is embedded as a resource, you should call this from your DllRegisterServer
implementation.LoadTypeLib
. This gives you an ITypeLib
GetTypeInfoOfGuid
.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With