none
Calling IDispatch::Invoke on managed object returns DISP_E_MEMBERNOTFOUND, how to pass correct interface pointer? RRS feed

  • Question

  • Hello,

    I have a managed class written in C# that implements an interface defined by an IDL file which derives from IDispatch.

    This is how the interface appears when imported to managed code:
    [TypeLibType(4160)] 
    [Guid("<GUID Omitted>")] 
    public interface IConnectionLostEvents 
         [DispId(1)] 
         void ConnectionInterrupted(); 
         [DispId(2)] 
         void ConnectionRestored(); 

    This is how my managed class looks (omitting code that isn't relevant to this question):
    Guid("<GUID Omitted>"), 
    ClassInterface(ClassInterfaceType.AutoDispatch), 
    public class PrototypeClass : IConnectionLostEvents 
    {
            public void ConnectionInterrupted() 
            { 
                MessageBox.Show("Oh no! We lost the connection!"); 
            } 
     
            public void ConnectionRestored() 
            { 
                MessageBox.Show("Phew! We got the connection back!"); 
            } 


    I am trying to interoperate with existing COM code used to call the events on this IConnectionLostEvents interface. I subscribe to these events by calling a subscribe method, which takes in an IDispatch pointer, and then uses IDispatch::Invoke() to invoke these methods on that interface.

    Method definition for subscribing to events:
    STDMETHODIMP <class name omitted>::SubscribeToEvent(IN BSTR bstrIdSubscriber, 
                                                      IN BSTR bstrIdPublisher, 
                                                      IN BSTR bstrIdEvent, 
                                                      IN IDispatch*   pidispSubscriberInterface) 
         //Stores the IDispatch pointer to later call events on. 

    The code for calling my events looks similar to the following, where eventHandler is the IDispatch pointer passed in above:
    hr = eventHandler->Invoke( pEventMsg->eventParams.dispidMember,  // DISPID  dispIdMember, 
                                                          pEventMsg->eventParams.iid,           // REFIID  riid, 
                                                          pEventMsg->eventParams.lcid,          // LCID  lcid, 
                                                          pEventMsg->eventParams.wFlags,        // WORD  wFlags, 
                                                          pEventMsg->eventParams.pdispparams,   // DISPPARAMS FAR*  pDispParams, 
                                                          pEventMsg->eventParams.pvarResult,    // VARIANT FAR*  pVarResult, 
                                                          pEventMsg->eventParams.pexcepinfo,    // EXCEPINFO FAR*  pExcepInfo, 
                                                          pEventMsg->eventParams.puArgErr);     // unsigned int FAR*  puArgErr 

    This call always returns DISP_E_MEMBERNOTFOUND when called on my .NET component. The dispid being used is 1 in this case, corresponding to the ConnectionInterrupted() method in the interface.

    My call to SubscribeToEvent looks like this from C#. I tried both ways:
    _subscribeService.SubscribeToEvent(_subscriberId, publisherId, eventsGuid, this); 
     
    -OR- 
     
    _subscribeService.SubscribeToEvent(_subscriberId, publisherId, eventsGuid, new DispatchWrapper(this)); 

    I've been trying to understand this whole thing for a while now and haven't come any closer to figuring out why this isn't working. One thought I had was that I needed to pass a pointer to the IConnectionLostEvents interface instead of this in the call to SubscribeToEvent() but I'm not sure how to do that in C#.

    Any help would be much appreciated!
    Thursday, March 19, 2009 1:59 PM

Answers

  • Well I ended up solving my problem. Posting the solution here for anyone else that has this issue.

    The reason that the member was not being found on my object, is that it was not using DispId(1) as the interface defined. Instead the CCW was auto-generating some DispId based on whatever algorithm it uses. The COM object wasn't calling GetIDsOfNames() to find the correct DispId and was instead just assuming a DispId of 1 since that is what the interface defines.

    To solve this problem without modifying the COM code (which I couldn't do), I created a proxy class for each event interface I want to subscribe to.

        [ClassInterface(ClassInterfaceType.AutoDispatch)] 
        public class ConnectionLostEventsProxy : IConnectionLostEvents 
        { 
            public delegate void ConnectionInterruptedDelegate(); 
            public delegate void ConnectionRestoredDelegate(); 
     
            public event ConnectionInterruptedDelegate ConnectionInterruptedEvent; 
            public event ConnectionRestoredDelegate ConnectionRestoredEvent; 
     
            [DispId(1)] 
            public void ConnectionInterrupted() 
            { 
                if (ConnectionInterruptedEvent != null
                { 
                    ConnectionInterruptedEvent(); 
                } 
            } 
     
            [DispId(2)] 
            public void ConnectionRestored() 
            { 
                if (ConnectionRestoredEvent != null
                { 
                    ConnectionRestoredEvent(); 
                } 
            } 
        } 

    I then pass this object in to the SubscribeToEvent method. Since this proxy has the DispId's set in the class definition the CCW doesn't generate new ones and Invoking the events works fine.
    • Marked as answer by Scott Richter Thursday, March 19, 2009 7:15 PM
    Thursday, March 19, 2009 7:15 PM