2

I have a lot of experience in C++ but am new to COM, MFC and MIDL. I'm trying to create a 32 bit COM server from an IDL file supplied by a third party. The IDL section looks like this:

[uuid(3CA6AC95-6F14-4D36-93BC-A9AAC9230FEA)]
dispinterface IMyEvent
{
    properties:
    methods:
        [id(1), helpstring("do the connect")]
            SCODE Connect([in] IDispatch *GetBackToMe);
        [id(2), helpstring("do the disconnect")]
            SCODE Disconnect();
};
[uuid(5695B1AB-0D7C-4C8B-AE07-6BF8F8B10393)]
coclass MyEvent
{
    [default] dispinterface IMyEvent;
};

MIDL generated the following:

MIDL_DEFINE_GUID(IID, DIID_IMyEvent, 0x3CA6AC95, 0x6F14, 0x4D36, 0x93, 0xBC, 0xA9, 0xAA, 0xC9, 0x23, 0x0F, 0xEA);
MIDL_DEFINE_GUID(CLSID, CLSID_MyEvent, 0x5695B1AB, 0x0D7C, 0x4C8B, 0xAE, 0x07, 0x6B, 0xF8, 0xF8, 0xB1, 0x03, 0x93);
typedef interface IMyEvent IMyEvent;
typedef class MyEvent MyEvent;
EXTERN_C const IID DIID_IMyEvent;
MIDL_INTERFACE("3CA6AC95-6F14-4D36-93BC-A9AAC9230FEA")
    IMyEvent : public IDispatch
{
};
EXTERN_C const CLSID CLSID_MyEvent;

I have written the following so far:

class MyEvent
{
public:
    SCODE Connect(IDispatch *GetBackToMe);
    SCODE Disconnect();
};
SCODE MyEvent::Connect(IDispatch *GetBackToMe)
{
    // TODO
    return 0;
}
SCODE MyEvent::Disconnect()
{
    // TODO
    return 0;
}

What glue do I need to make this work?

5
  • 1
    Don't use MFC to implement COM interfaces, you really should use ATL. And consider COM to be an adapter layer on top of your C++ business logic. E.g. translate exceptions to HRESULT, translate COM types to C++ types etc. Have a look at Active Template Library (ATL) Tutorial. You start by creating an IDL file then building a tlb from that which you wrap it in a resource dll so you can register the typelibrary. Afer that you can implement with ATL and using #import. Commented Mar 4 at 15:54
  • MyEvent must implement IMyEvent, among other things. PS: you should use what's called connection points to do callbacks devblogs.microsoft.com/oldnewthing/20130611-00/?p=4113 Commented Mar 4 at 15:58
  • 1
    Further resources : ATL::IDispatchImp, and Using IDispEventImpl ... be prepared for a bit of a learning curve to do it right. Commented Mar 4 at 15:59
  • Thanks @PepijnKramer, I had been wondering for a long time whether I should be using MFC or ATL and you have answered that well. It seems that ATL is preferred for simple use cases, which mine is. Commented Mar 6 at 13:23
  • You're welcome :) Commented Mar 6 at 13:32

1 Answer 1

2

Your MyEvent class needs to implement the IMyEvent interface. That means deriving from the interface type, and defining implementations of its methods. Also since IMyEvent is a dispinterface, that means you also need to implement all of the methods that are inherited from IUnknown and IDispatch, too:

class MyEvent : public IMyEvent
{
private
    ULONG m_refCnt = 0;

public:
    MyEvent() = default;

    // IUnknown
    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
    ULONG STDMETHODCALLTYPE AddRef() override;
    ULONG STDMETHODCALLTYPE Release() override;

    // IDispatch
    HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) override;
    HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) override;        
    HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) override;
    HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) override;

    // IMyEvent
    SCODE Connect(IDispatch *GetBackToMe) override;
    SCODE Disconnect() override;
};
HRESULT STDMETHODCALLTYPE MyEvent::QueryInterface(REFIID riid, void **ppvObject)
{
    if (!ppvObject) return E_POINTER;
    *ppvObject = nullptr;
    if (riid == DIID_IMyEvent)
        *ppvObject = (IMyEvent*)this;
    else if (riid == IID_IDispatch)
        *ppvObject = (IDispatch*)(IMyEvent*)this;
    else if (riid == IID_IUnknown)
        *ppvObject = (IUnknown*)(IMyEvent*)this;
    else
        return E_NOINTERFACE;
    AddRef();
    return S_OK;
}

ULONG STDMETHODCALLTYPE MyEvent::AddRef()
{
    return ++m_refCnt;
}

ULONG STDMETHODCALLTYPE MyEvent::Release()
{
    if (--m_refCnt == 0) {
        delete this;
        return 0;
    }
    return m_refCnt;
}

HRESULT STDMETHODCALLTYPE MyEvent::GetTypeInfoCount(UINT *pctinfo)
{
    // TODO
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyEvent::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
{
    // TODO
    return E_NOTIMPL;
}   

HRESULT STDMETHODCALLTYPE MyEvent::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
{
    // TODO
    return E_NOTIMPL;
}

HRESULT STDMETHODCALLTYPE MyEvent::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
    // TODO
    return E_NOTIMPL;
}

SCODE MyEvent::Connect(IDispatch *GetBackToMe)
{
    // TODO
    return 0;
}

SCODE MyEvent::Disconnect()
{
    // TODO
    return 0;
}

You also need to implement and register a class that implements the IClassFactory or IClassFactory2 interface, so that COM can create instances of your MyEvent class when requested.

See MSDN for more details:

Component Object Model (COM)

COM Clients and Servers

COM Server Responsibilities

Sign up to request clarification or add additional context in comments.

4 Comments

Hey Remy, nice work. But with ATL you can just derive from CRTP/mixin class ATL::IDispatchImpl : learn.microsoft.com/en-us/cpp/atl/reference/… which I think is a lot less verbose :)
@PepijnKramer yes, using a framework like ATL to wrap much of the work is a good idea. But having a good understanding of what's actually happening under the hood is important, too.
I kind of agree, but after working with COM from since 1998... It is in the end a complex tech. SO if OP only needs to do this once. Going into detail how COM hooks into C++ might not be necessary, and the somewhat more declaritve style of ATL might be less frustrating. And normally I would even advise agains using the whole IDispatch machinery but seems OP got that as a given.
The naming in the idl file is a bit unfortunate too , since this is not a (connectionpoint) event but just a regular callback interface. But sometimes it is what it is :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.