6

I have managed to create a C# COM object with events. Please find code below,

    [Guid("1212674-38748-45434")]
    public interface ICalculator
    {
        int Add(int Num1, int Num2);
    }

    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    [Guid("3453674234-84444-84784")]
    public interface ICalculatorEvents
    {
        [DispId(1)]
        void Completed(int Result);
    }

    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(ICalculatorEvents))]
    [Guid("87457845-945u48-4954")]
    public class Calculator : ICalculator
    {
        public delegate void CompletedDelegate(int result);
        public event CompletedDelegate Completed;
        public Add(int Num1, int Num2)
        {
            int Result = Num1 + Num2;
            if(Completed != null)
                Completed(Result);
        }
    }

I have imported this COM object in a C++ console application and able to call the 'Add()' method. I am not sure how to handle 'Completed' event in my C++ application. Can you please advise on this? I am looking to display the result value in console whenever this event occurs.

Please find C++ application's code below. The Event 'Completed' never gets handled here. This goes into an infinite loop.

    #import "Calculator.tlb"
    using namespace Calculator;
    int Flag = 0;
    class HandleEvent : public ICalculatorEvent
    {
        public:
            HandleEvent(void);
            ~HandleEvent(void);
            HRESULT __stdcall QueryInterface(const IID &, void **);
            ULONG __stdcall AddRef(void) { return 1; }
            ULONG __stdcall Release(void) { return 1; }
            HRESULT __stdcall Completed(int Result);
    };

    HandleEvent::HandleEvent(void)
    {
    }

    HRESULT HandleEvent::Completed(int Result)
    {
        printf("Addition Completed, Result: %d", Result);
        Flag = 1;
    }

    HRESULT HandleEvent::QueryInterface(const IID & iid,void ** pp)
    {
        if (iid == __uuidof(ICalculatorEvent) || iid == __uuidof(IUnknown))
        {
            *pp = this;
            AddRef();
            return S_OK;
        }
        return E_NOINTERFACE;
    }

    int _tmain(int argc, _TCHAR* argv[])
    {
        CoInitialize(NULL);
        Flag = 0;
        ICalculatorPtr pCalc(__uuidof(Calculator));
        pCalc->Add(5, 6);

        do
        {
        }while(Flag == 0);

        CoUninitialize ();
        return 0;
    }

Thanks in advance.

5
  • I think you Completed event object won't get called because it is always null. Which class is implemented ICalculatorEvents interface ? Commented Jun 6, 2012 at 23:35
  • ICalculatorEvents is implemented in C++ application. Please find C++ code below, Commented Jun 6, 2012 at 23:40
  • sorry i couldnot add the code in the comment. so I have added this to my first post. Thanks Commented Jun 6, 2012 at 23:46
  • In _tmain() function, how pCalc->Add() should know which ICalculatorEvent implemented object should be called ? There is no EventHandler object here. If there was, in Add() method, Completed delegate doesn't point to any method. Commented Jun 6, 2012 at 23:52
  • I assumed that the method, HandleEvent::Completed(int Result) would be called when the event is invoked in Add method? I am completely new to COM and I think I am missing to understand something here. Commented Jun 7, 2012 at 0:03

2 Answers 2

0

If you want to use delegates you don't need to declare an interface. Change _tmain() function like this :

int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(NULL);
    Flag = 0;

    EventHandler evh ;
    ICalculatorPtr pCalc(__uuidof(Calculator));
    pCalc->Completed = &evh.Completed() ;
    pCalc->Add(5, 6);

    do
    {
    }while(Flag == 0);

    CoUninitialize ();
    return 0;
}

If you want to use an interface try this.

 [ClassInterface(ClassInterfaceType.None)]
 [ComSourceInterfaces(typeof(ICalculatorEvents))]
 [Guid("87457845-945u48-4954")]
 public class Calculator : ICalculator
 {
     public ICalculatorEvents callbackObject ;

     public Add(int Num1, int Num2)
     {
         int Result = Num1 + Num2;
         if(callbackObject != null)
             callbackObject.Completed(Result);
     }
 }

and change _tmain() method to this.

int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(NULL);
    Flag = 0;

    EventHandler evh ;
    ICalculatorPtr pCalc(__uuidof(Calculator));
    pCalc->callbackObject = &evh ;
    pCalc->Add(5, 6);

    do
    {
    }while(Flag == 0);

    CoUninitialize ();
    return 0;
}
Sign up to request clarification or add additional context in comments.

1 Comment

Should "EventHandler" be some global class or is it something you've defined yourself? VS2019 tells me "identifier is undefined". Do I need to include some header?
0

I found that COM initialization in C++ client should be done using

    CoInitializeEx(NULL, COINIT_MULTITHREADED);

for asynchronous event processing from C# (.NET) COM server, otherwise C++ client receives events only after CoUninitialize() call.

Events handling class:

    class EventWrapper : public IDispEventSimpleImpl<1, EventWrapper, &DIID_RumCardCOMEvents >
    {
    public:
        // now you need to declare a sink map - a map of methods handling the events
        BEGIN_SINK_MAP(EventWrapper)
            SINK_ENTRY_INFO(1, DIID_RumCardCOMEvents, 0x1, isCardInserted, &cardInserted)
            SINK_ENTRY_INFO(1, DIID_RumCardCOMEvents, 0x2, isCardRemoved, &cardRemoved)
            // event interface id (can be more than 1)---+      |      |                   |
            // must match dispid of your event -----------------+      |                   |
            // method which handles the event  ------------------------+                   |
            // type information for event, see below --------------------------------------+
        END_SINK_MAP()

    // declare the type info object. You will need one for each method with different signature.
        // it will be defined in the .cpp file, as it is a static member
        static _ATL_FUNC_INFO cardInserted;  // 'placeholder' object to carry event information (see below)
        static _ATL_FUNC_INFO cardRemoved;  // 'placeholder' object to carry event information (see below)

        // method which handles the event
        STDMETHOD (isCardInserted)(unsigned char type)
        { 
            // usually it is defined it in the .cpp file
            cout << "isCardInserted: " << (int)type << endl;
            return 0;
        }

        STDMETHOD (isCardRemoved)()
        { 
            // usually it is defined it in the .cpp file
            cout << "isCardRemoved" << endl;
            return 0;
        }
    };

Main:

    int main()
    {
        CoInitializeEx(NULL, COINIT_MULTITHREADED);
        try
        {
            EventWrapper ev;
            ev.DispEventAdvise(/*COM interface*/);
            // receiving events
            ev.DispEventUnadvise(/*COM interface*/);
        }
        catch (_com_error& e)
        {
            cout << "Exception: " << e.ErrorMessage() << endl;
        }

        CoUninitialize();
        return 0;
    }

2 Comments

What is "&DIID_RumCardCOMEvents"? Is RumCardCOMEvents your COM event interface? Where does the "DIID" part come from?
Yes it is COM event interface, sorry don't remember details, it was too long ago.

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.