Implement COM IDispatch without ATL

2020-02-23 08:33发布

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:

  • It is better practice to delegate the IDispatch method calls to some ITypeInfo. Is this correct?
  • If so, how to get an ITypeInfo to myself? LoadTypeLib() and family (followed by looking at ITypeLib::GetTypeInfo())?
  • If not, how is it implemented properly? Links to good-quality documentation and self-contained examples are of much use.

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?

标签: c++ c qt com
3条回答
The star\"
2楼-- · 2020-02-23 08:53

What you can do is use a Type Library.

If you have one, that's one thing you won't have to do. If you don't have one, then you can create one using the MIDL compiler. That's a free tool that comes with the Platform SDK. Of course in this case, it will mean you will have to write an IDL file (which can be a lot of work but you only need to define what you want). Depending on the kind of COM object you're targeting, an IDL file may already be available in the SDK. Once you have that IDL ready, you can compile it and get back a TLB file.

Once you have that TLB file, you can load it using the LoadTypeLib function. Once you have an ITypeLib reference, you can load the ITypeInfo that you need (there may be more than once), and basically route IDispatch calls (GetIDsOfNames, etc.) into ITypeInfo implementation calls, as they are very similar.

查看更多
闹够了就滚
3楼-- · 2020-02-23 09:14

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:

  1. Write an IDL file which declares your interface as a dual interface. It has to be a dual interface, as that is how the 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.
  2. Compile the IDL to a header file and type library as part of the build process
  3. The header file produced from the IDL defines the interface, which your implementing class must inherit from. Once you have implemented all the methods you are good to go. (For development start by making them all return E_NOTIMPL then implement them one by one)
  4. Install the Type Library, either to the destination directory, or as a resource in the EXE/DLL. It will need to be registered by calling RegisterTypeLib. If it is embedded as a resource, you should call this from your DllRegisterServer implementation.
  5. Load the type library when the first instance of your object is created, using LoadTypeLib. This gives you an ITypeLib
  6. Get the ITypeInfo you need using GetTypeInfoOfGuid.
查看更多
Fickle 薄情
4楼-- · 2020-02-23 09:15

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.

查看更多
登录 后发表回答