none
Dot Net interop event sink RRS feed

  • Question

  • Hello,

    I am using C# library in c++ code, I am using Event sink technique to receive event and data from c# code,

    Is there any way to get confirmation detail in C# code that event has been received in c++ code.

    Thanks in advance!!!

    Friday, September 19, 2014 12:42 PM

Answers

  • Hello yashzee,

    1. >> Is there any way to get confirmation detail in C# code that event has been received in c++ code.

    1.1 You can get confirmation that at least one client has connected to the events of a C# class by checking the event delegate whether it is non-null.

    1.2 As to whether the event delegate has successfully resulted in the client event handler being executed, I think you could check for any exception.

    1.3 Note also that an event source could be connected to more than one sink and you generally cannot identify who the client sinks are.

    2. Some suggestions you can consider :

    2.1 If an event method has an "out" parameter which requires a sink to fill before returning, it could be used to indicate confirmation.

    2.2 If you have the source codes to the C++ sink code, you can do simple things like :

    - Logging by writing text output to an external file.

    - Display a message box for confirmation.

    Hope this helps,

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    • Marked as answer by yashzee Tuesday, September 23, 2014 7:57 PM
    Friday, September 19, 2014 5:25 PM

All replies

  • Hello,

    I am using C# library from c++ code, I am using Event sink technique to receive event in c++ thrown from C# code,

    Is there any mechanism to get confirmation detail in C# code that event has been received in c++.

    Thanks in advance!!!

    Friday, September 19, 2014 12:31 PM
  • This is a duplicate of this post.

    Friday, September 19, 2014 2:09 PM
  • You can turn on native debugging and set a breakpoint in the C++ code (which is presumably native code, and why you are having trouble.)

    Project > Properties > Debug > Enable Native Code Debugging.

    Friday, September 19, 2014 2:11 PM
  • Hello yashzee,

    1. >> Is there any way to get confirmation detail in C# code that event has been received in c++ code.

    1.1 You can get confirmation that at least one client has connected to the events of a C# class by checking the event delegate whether it is non-null.

    1.2 As to whether the event delegate has successfully resulted in the client event handler being executed, I think you could check for any exception.

    1.3 Note also that an event source could be connected to more than one sink and you generally cannot identify who the client sinks are.

    2. Some suggestions you can consider :

    2.1 If an event method has an "out" parameter which requires a sink to fill before returning, it could be used to indicate confirmation.

    2.2 If you have the source codes to the C++ sink code, you can do simple things like :

    - Logging by writing text output to an external file.

    - Display a message box for confirmation.

    Hope this helps,

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    • Marked as answer by yashzee Tuesday, September 23, 2014 7:57 PM
    Friday, September 19, 2014 5:25 PM
  • Hi Bio,

    I am trying point 2.1, I used an out parameter in event how we can fill that parameter in c++, I am getting exception as heap corrupt or memory access violation.

    Wednesday, September 24, 2014 5:39 AM
  • Hello yashzee,

    1. Sorry for my late reply.

    2. Let me illustrate with some code. Let's say we have a COM-visible C# class that supports some COM-visible events :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace YashzeeClassLibrary
    {
        [ComVisible(true)]
        [Guid("854FA8F1-9E4F-4c28-AA44-2C285A074423")]
        [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
        public interface IYashZeeEvents
        {
            [DispId(1)]
            void Event01([Out] out int iAcknowledgement);
            [DispId(2)]
            void Event02([Out] out string strAcknowledgement);
        }
    
        // Delegates for the events.
        public delegate void Event01Handler([Out] out Int32 iAcknowledgement);
        public delegate void Event02Handler([Out] out string strAcknowledgement);
    
        [ComVisible(true)]
        [Guid("877768F8-5A97-4d51-AB93-5BA57A496D78")]
        [ComSourceInterfaces(typeof(IYashZeeEvents))]
        [ClassInterface(ClassInterfaceType.AutoDual)]
        public class Yashzee
        {
            public event Event01Handler event01;
            public event Event02Handler event02;
    
            public Yashzee()
            {
            }
    
            public void Method01()
            {
                if (event01 != null)
                {
                    Int32 iAcknowledgement;
    
                    event01(out iAcknowledgement);
    
                    Console.WriteLine("iAcknowledgement : {0:D}", iAcknowledgement);
                }
            }
    
            public void Method02()
            {
                if (event02 != null)
                {
                    string strAcknowledgement;
    
                    event02(out strAcknowledgement);
    
                    Console.WriteLine("strAcknowledgement : {0:S}", strAcknowledgement);
                }
            }
        }
    }
    

    2.1 Here, we have a C# class named Yashzee which supports the event interface IYashZeeEvents.

    2.2 Notice that IYashZeeEvents is declared to be a dispinterface ([InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]). I assume that your events are dispinterface based.

    2.3 Notice that the 2 methods of the IYashZeeEvents event each takes an "out" parameter.

    2.4 Hence it is the event sink (also known as the event handler) which must fill in this "out" parameter.

    2.5 The event source (the C# class instance) will receive this value.

    3. Now over in the client side (the C++ code which contains the event sink), we must fill in the required "out" parameters.

    4. What happens is that because IYashZeeEvents is an IDispatch interface, the C++ client side would have to deal with a dispinterface.

    5. I shall return with another post showing how the event handler in C++ will deal with the "out" parameters.

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Thursday, September 25, 2014 4:44 PM
  • Hello yashzee,

    1. If an event interface is dispinterface-based (as it usually is), then :

    1.1 Your event handler must be a class that implements IDispatch.

    1.2 In particular, when an event method is invoked, it is the Invoke() method of your event handler class that gets called.

    1.3 All event parameters are exchanged via VARIANTs.


    2. If an event interface has a method like the following :

    void SomeEvent(int iValue);

    then the value of iValue (an "in" parameter) will be passed to the event handler via a VARIANT of type VT_I4.


    3. Now if an event parameter is an "out" parameter, the VARIANT will be of type (VT_BYREF | *) where * can be any one of the variant types. Some examples :

    3.1 (VT_BYREF | VT_I4) for an "out" int.

    3.2 (VT_BYREF | VT_BSTR) for an "out" string.


    4. The following is the C++ code that I used to receive the IYashZeeEvents events :

    HRESULT CMFCClientDlg::Invoke
    (
      DISPID dispidMember, 
      REFIID riid, 
      LCID lcid, 
      WORD wFlags, 
      DISPPARAMS FAR* pdispparams, 
      VARIANT FAR* pvarResult, 
      EXCEPINFO FAR* pexcepinfo, 
      unsigned int FAR* puArgErr 
    )
    {
      if (dispidMember == 0x01)  // Event1 event.
      {
    	// 1st param : [out] long * iAcknowledgement.		
    	VARIANT*	pvar = &((pdispparams -> rgvarg)[0]);
    	
    	*((long*)(V_I4REF(pvar))) = 100;
      }
      else if (dispidMember == 0x02)  // Event2 event.
      {
    	// 1st param : [out] BSTR * strAcknowledgement.		
    	VARIANT*	pvar = &((pdispparams -> rgvarg)[0]);
    	
    	*((BSTR*)(V_BSTRREF(pvar))) = ::SysAllocString(L"I am here.");
      }
    
      return S_OK;
    }
    

    4.1 All parameters to the event method are contained in the pdispparams parameter which is a pointer to a DISPPARAMS structure which in turn contains the field rgvarg which is an array of VARIANTARG.

    4.2 Now when the Event01() event is fired, the event handler (the C++ client) is expected to fill in the appropriate VARIANT parameter with a long value. The VARIANT parameter will be of type (VT_BYREF | VT_I4) and the way to fill in the required long value is in the plVal field of the VARIANT. This is the rationale behind the code :

    *((long*)(V_I4REF(pvar))) = 100;

    where we set the out value of 100 to the VARIANT.

    Note that the memory buffer that is meant to contain the "out" long value will have been allocated somewhere and the VARIANT's plVal field points to this memory buffer.

    This memory buffer is allocated by the .NET interop marshaler and will be de-allocated when the event method returns to the source (the C# code).

    4.3 When the Event02() event is fired, the event handler (the C++ client) is expected to fill in the appropriate VARIANT parameter with a pointer to a BSTR. The VARIANT parameter will be of type (VT_BYREF | VT_BSTR) and the way to fill in the required BSTR value is in the pbstrVal field of the VARIANT. This is the rationale behind the code :

    *((BSTR*)(V_BSTRREF(pvar))) = ::SysAllocString(L"I am here.");

    where we set a pointer to a BSTR to the VARIANT.

    Note that the memory buffer that is meant to contain the pointer to the BSTR will have been allocated somewhere and the VARIANT's pbstrVal field points to this 4-byte memory address.

    This 4-byte memory buffer is allocated by the .NET interop marshaler and will be de-allocated when the event method returns to the source (the C# code).

    Now, because we are now dealing with a BSTR, the interop marshaler will also be responsible for the deallocation of the BSTR itself. This will be done via ::SysFreeString() which is called by the interop marshaler.

    5. I hope this post will help you with the C++ coding. Post again if you need further clarification.

    - Bio.


    Please visit my blog : http://limbioliong.wordpress.com/

    Friday, September 26, 2014 4:59 PM