none
How to use a COM RegisterSink method for events... RRS feed

  • Question

  • I'm trying to use a COM interface that has a RegisterSink (and on a different interface a RegisterSecurity) method.  When I try and use them I'm not seeing any events being raised in my program.  Other interfaces in the same typelib use the 'normal' connection point stuff for events as far as I can see, and I _do_ manage to get callbacks on them.  I don't know why those two are different?  (Maybe that only one instance of each 'sink' is expected/allowed for them, whereas the connection point form is used where there can be multiple instances of the target and sink??)

    In the normal cases I just do e.g. the following and do see the event raised.

    _dun = new BTSTACKSERVERLib.BTDUNApi();
    
    _dun.StateChange += new BTSTACKSERVERLib._IBTDUNApiEvents_StateChangeEventHandler(_dun_StateChange);
    
    
    
    

    In the other two cases, the 'register' method appears in the typelib as

    [id(0x00000006), helpstring("method RegisterSink")]
    
    HRESULT RegisterSink([in] IDispatch* lpDispatch);
    
    
    
    

    What do I need in that case?

    [ComVisible(true)]
    
    [ClassInterface(ClassInterfaceType.AutoDispatch)]
    
    public class GapEvent2 : BTSTACKSERVERLib._IBTGAPEvents
    
    {
    
     ...
    
    }
    
    
    
    

    and

    _gap = new BTSTACKSERVERLib.BTGAPApi();
    
    _ge2a = new GapEvent2();
    
    var p = Marshal.GetIDispatchForObject(_ge2a);
    
    _gap.RegisterSink(p);
    
    
    
    

    Or something else?

    Also is there any way to get diagnostics etc?

    I can make the full typelib available if that helps...

    Alan


    http://www.alanjmcf.me.uk/ Please follow-up in the newsgroup. If I help, mark the question answered
    Thursday, April 21, 2011 10:53 AM

Answers

  • Hello Alan,

     

    1. >> I had wondered whether the RegisterSink method having an Object parameter in .NET would mean that the pointer was not going to be used directly.  I've tried just passing _ge2a directly and still no events unfortunately.  As far as you are aware that should be ok?  That the correct form of object/interface will be automatically created for IDispatch when passed to that object parameter?

    1.1 For COM interface methods as well as unmanaged APIs, parameters of IDispatch pointer types (i.e. IDispatch*) are directly mapped to managed object types.

    1.2 So yes, please do pass "_ge2a" directly from now onwwards. The use of "p" (the return value of Marshal.GetIDispatchForObject(_ge2a)) is incorrect.

    1.3 Please remember that the IntPtr type is also a managed type and so it is treated as an object. If you pass an IntPtr object as a parameter to a COM method that expects an IDispatch pointer, it is that IntPtr object (that internally contains a mere memory address number) that gets passed to the method, not the object that it points to.

     

    2. Other Possible Reasons For Failure To Receive Events

    2.1 Another possible reason for the failure to receive _IBTGAPEvents events is that the BTGAPApi object did try to fire an event but the event method invokation failed.

    2.2 This could be due to invalid parameter declaration on the managed side. Or invalidly attributed parameter declarations (i.e. invalid use of the MarshalAsAttribute).

    2.3 Note that failure of dispinterface method invokation from unmanaged code to managed code (where the dispinterface is implemented on the managed side) are usually silent. That is, no exceptions are thrown.

     

    Best of luck, Alan,

    - Bio.

     

    • Marked as answer by Paul Zhou Friday, April 29, 2011 3:10 AM
    Friday, April 22, 2011 5:17 AM

All replies

  • Hello Alan,

     

    1. Potential Source Of The Problem

    1.1 One potential problem that I noticed is with the following code :

    _gap = new BTSTACKSERVERLib.BTGAPApi();

    _ge2a = new GapEvent2();

    var p = Marshal.GetIDispatchForObject(_ge2a);

    _gap.RegisterSink(p);

     

    1.2 Examine the following :

    var p = Marshal.GetIDispatchForObject(_ge2a);

    Here "p" will receive an IntPtr of the IDispatch interface pointer from "_ge2a". OK so far.

     

    1.3 However, when we pass "p" to the RegisterSink() method :

    _gap.RegisterSink(p);

    what happens here is that it will not be the IDispatch pointer of "_ge2a" that will be passed to RegisterSink().

    Rather, it is the IDispatch pointer of "p" (i.e. the object behind the IntPtr "p") that gets passed to RegisterSink().

     

    1.4 This is because the RegisterSink() method, which appears in the IDL as :

    [id(0x00000006), helpstring("method RegisterSink")]
    HRESULT RegisterSink([in] IDispatch* lpDispatch);

    when imported into its managed equivalent, becomes :

    void RegisterSink(object lpDispatch);

    Hence, by passing "p" into RegisterSink(), "p" will first be cast into an object.

    Then the IDispatch pointer of this object (originating from "p", not "_ge2a") will get passed to RegisterSink().

    In short what gets passed to the method is an IntPtr object, not a GapEvent2 object.

     

    1.5 Now in the implementation code for the RegisterSink() method, an IDispatch pointer is received and will probably be kept somewhere inside the code for the BTGAPApi COM object. Then, when the BTGAPApi object later tries to use it to fire an event of the _IBTGAPEvents dispinterface, it will fail in one of 3 possible ways :

    1.5.1 It will fail when it tries to QueryInterface() the IDispatch pointer for the _IBTGAPEvents dispinterface.

    1.5.2 It will fail when it tries to get the dispid of the name of the event that it wants to fire (via IDispatch::GetIDsOfNames()).

    1.5.3 It will fail when it tries to call IDispatch::Invoke() using a hardcoded dispid for the event that it wants to fire.

    Each of the above reasons are directly related to the fact that the IDispatch pointer originates from the IntPtr object "p" and not from the GapEvent2 object "_ge2a".

     

    1.6 One solution to this is to use the "_ge2a" object directly as the parameter for RegisterSink(). Your code should be written as :

    _gap = new BTSTACKSERVERLib.BTGAPApi();

    _ge2a = new GapEvent2();

    _gap.RegisterSink(_ge2a);

    i.e. no need to use Marshal.GetIDispatchForObject().

     

    2. Other Potential Problems

    2.1 Of course, even if you had made the code changes recommended in point 1.6, the anticipated events may still not fire. There could still be other problems.

    2.2 In this case, one questions I would like to ask is : have you ever used the COM object in any unmanaged client apps before ? Have the events of _IBTGAPEvents ever been successfully fired before ?

    2.3 If problems persist, perhaps you could contact the vendor of the COM object.

     

    - Bio.

     

    Thursday, April 21, 2011 4:38 PM
  • Thanks for the very thorough answer.

    I had wondered whether the RegisterSink method having an Object parameter in .NET would mean that the pointer was not going to be used directly.  I've tried just passing _ge2a directly and still no events unfortunately.  As far as you are aware that should be ok?  That the correct form of object/interface will be automatically created for IDispatch when passed to that object parameter?

    You other questions are very perceptive. :-)  I have _not_ seen the API work elsewhere.  I have tried to create a C++ client but whereas my C skills are good, my C++ are not.  I found it hard to even find the correct way to pass byte arrays and GUIDs to the COM methods, so got no further.  I didn't think I'd be able to find out how to create the sink class either, so have stopped for the moment.  I've got a COM book being delivered so will have another go after I'm had a read of it.

    The situation with the vendor is a little odd -- as always. :-)  Their public API is a set of (non-COM) C++ classes, and their documentation says that those classes call the COM API.  Unfortunately the C++ API supports less than the COM API does.  I'll drop them an email but suspect that they'll say: we only support the C++ api. :-(  BTW 'they' are Widcomm, or Broadcom Bluetooth as they are now...

    Thanks
    Alan

     


    http://www.alanjmcf.me.uk/ Please follow-up in the newsgroup. If I help, mark the question answered
    Thursday, April 21, 2011 7:51 PM
  • Hello Alan,

     

    1. >> I had wondered whether the RegisterSink method having an Object parameter in .NET would mean that the pointer was not going to be used directly.  I've tried just passing _ge2a directly and still no events unfortunately.  As far as you are aware that should be ok?  That the correct form of object/interface will be automatically created for IDispatch when passed to that object parameter?

    1.1 For COM interface methods as well as unmanaged APIs, parameters of IDispatch pointer types (i.e. IDispatch*) are directly mapped to managed object types.

    1.2 So yes, please do pass "_ge2a" directly from now onwwards. The use of "p" (the return value of Marshal.GetIDispatchForObject(_ge2a)) is incorrect.

    1.3 Please remember that the IntPtr type is also a managed type and so it is treated as an object. If you pass an IntPtr object as a parameter to a COM method that expects an IDispatch pointer, it is that IntPtr object (that internally contains a mere memory address number) that gets passed to the method, not the object that it points to.

     

    2. Other Possible Reasons For Failure To Receive Events

    2.1 Another possible reason for the failure to receive _IBTGAPEvents events is that the BTGAPApi object did try to fire an event but the event method invokation failed.

    2.2 This could be due to invalid parameter declaration on the managed side. Or invalidly attributed parameter declarations (i.e. invalid use of the MarshalAsAttribute).

    2.3 Note that failure of dispinterface method invokation from unmanaged code to managed code (where the dispinterface is implemented on the managed side) are usually silent. That is, no exceptions are thrown.

     

    Best of luck, Alan,

    - Bio.

     

    • Marked as answer by Paul Zhou Friday, April 29, 2011 3:10 AM
    Friday, April 22, 2011 5:17 AM