//
you're reading...
.NET Events, Delegates, Programming Issues/Tips

Handling .NET Events in Unmanaged Code using Event Accessors.

1. Introduction.

1.1 In the .NET framework, events are activated and handled using the event-delegate model.

1.2 Such a system is alien to unmanaged code.

1.3 However, there are ways to bridge .NET events to the unmanaged world. COM interop (via the use of the ComSourceInterfacesAttribute) being the conventional way.

1.4 The specific technique used is the COM connection point protocol which involves the event sources and sinks.

1.5 A managed class which wants to source events must be declared with the ComSourceInterfacesAttribute with a COM-visible source interface being indicated. An unmanaged client application which wants to sink events must provide a sink object which implements the source interface (see Supporting COM Events from a Managed Class for more information).

1.6 I was curious as to whether it was possible to handle a .NET event in an unmanaged global function via .NET Delegates hooked through the use of event accessors.

1.7 One advantage to be gained from this is the avoidance of the need to create a COM sink object to implement the source interface.

1.8 The use of a global function to handle a .NET event also parallels the .NET event model which does not necessitate the involvement of any class when specifying the event handling delegate function. After all, a .NET delegate provides functionality akin to a function pointer.

1.9 Through investigative work and experiments, I discovered a way to accomplish this without too much effort. This blog presents the result of my research.

2. .NET Event Accessors.

2.1 A public event member of a (COM-visible) managed class will be exposed as a pair of COM methods known as event accessors.

2.2 For illustrative purposes, I have listed below the source codes of a COM-visible C# class which exports an event object :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace CSTestServer
{
    [ComVisible(true)]
    [Guid("72DA8C7E-1633-4685-A74F-222B9E9A67FB")]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class CSTestServerClass
    {
        // A managed delegate must be declared in order
        // to trigger events in the first place.
        // It is the use of the ComSourceInterfacesAttribute
        // together with the ITestEvents argument that links
        // the delegate to the COM event.
        public delegate void TestEvent01_Delegate();
        // In order that unmanaged client apps be able to access
        // the event delegate, it must be declared as "public".
        // When this class library has been type library
        // exported (via REGASM.EXE or TLBEXP.EXE), there will
        // be two additional methods add_TestEvent01()
        // and remove_TestEvent01() from the class interface.
        public event TestEvent01_Delegate TestEvent01;

        // Defined a method to test trigger the TestEvent01 event.
        public void TriggerEvent()
        {
            // As long as a client application has connected
            // with the events of this class, the event delegate
            // TestEvent01 will be non-Null.
            if (TestEvent01 != null)
            {
                TestEvent01();
            }
        }
    }
}

This set of codes is intended to be part of a class library project which, when compiled, will produce CSTestServer.dll.

The following is a general summary of the CSTestServerClass class :

  • It is COM-visible which makes it createable from a COM client.
  • It exposes an event object named “TestEvent01”.
  • It also exposes a method named TriggerEvent() which is used to fire all handlers of the “TestEvent01” event.

2.3 “TestEvent01” is of type “TestEvent01_Delegate” :

public delegate void TestEvent01_Delegate();

It is expected to be hooked through the normal .NET event accessor (“+=” in C# syntax or the AddHandler statement in VB.NET).

2.4 After successful compilation and type library exportation (either by the use of REGASM.EXE with the /tlb option or by the use of TLBEXP.EXE), the following will be the IDL listing for the _CSTestServerClass class interface for the CSTestServerClass coclass :

    [
      odl,
      uuid(6EB97830-1900-3587-AA0D-30D26EA2B413),
      hidden,
      dual,
      nonextensible,
      oleautomation,
        custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "CSTestServer.CSTestServerClass")    

    ]
    interface _CSTestServerClass : IDispatch {
        [id(00000000), propget,
            custom({54FC8F55-38DE-4703-9C4E-250351302B1C}, "1")]
        HRESULT ToString([out, retval] BSTR* pRetVal);
        [id(0x60020001)]
        HRESULT Equals(
                        [in] VARIANT obj,
                        [out, retval] VARIANT_BOOL* pRetVal);
        [id(0x60020002)]
        HRESULT GetHashCode([out, retval] long* pRetVal);
        [id(0x60020003)]
        HRESULT GetType([out, retval] _Type** pRetVal);
        [id(0x60020004)]
        HRESULT add_TestEvent01([in] IUnknown* value);
        [id(0x60020005)]
        HRESULT remove_TestEvent01([in] IUnknown* value);
        [id(0x60020006)]
        HRESULT TriggerEvent();
    };

2.5 Note the add_TestEvent01() and remove_TestEvent01() methods. These methods correspond directly to the .NET event accessors which are used to setup and remove event handlers through the “+=” and the “-= ” syntices respectively in C# or the AddHandler and RemoveHandler statements respectively in VB.NET.

2.6 They are listed as part of the _CSTestServerClass class interface because the TestEvent01 event member has been declared public.

2.7 The question now is : how do we use these accessors to hook an unmanaged function to the TestEvent01 event ?

2.8 These accessors each require a pointer to an IUnknown interface. More specifically a pointer to the IUnknown interface of a .NET delegate object. This requires a COM-visible .NET delegate object.

2.9 How do we acquire a COM-visible .NET delegate object in unmanaged code ? And how would we associate this delegate with an unmanaged function ?

2.10 As it turned out, it is not very difficult to obtain a .NET delegate object which is associated with an unmanaged function. The next section explains this in greater detail.

3. Obtaining a .NET Delegate Object in Unmanaged Code.

3.1 As of this writing (circa November 2011), I have not found a way to directly instantiate a .NET delegate as a COM object in unmanaged code.

3.2 However, the .NET delegate class itself is declared COM-visible :

[SerializableAttribute]
[ComVisibleAttribute(true)]
[ClassInterfaceAttribute(ClassInterfaceType.AutoDual)]
public abstract class Delegate : ICloneable, ISerializable

which means that a managed object derived from the Delegate class can survive in unmanaged code wearing a COM-callable-wrapper.

3.3 Therefore one possibility for obtaining a delegate in unmanaged code is to call a method from a COM-visible managed object that returns a delegate.

3.4 Aside from this, there is another requirement that we will need to fulfill : associating a .NET delegate with an unmanaged function.

3.5 In managed code programming, the conventional way to associate an unmanaged function with a .NET delegate is by using the Marshal.GetDelegateForFunctionPointer() method :

public static Delegate GetDelegateForFunctionPointer(IntPtr ptr, Type t);

This method takes an IntPtr which is a pointer to the unmanaged function that will serve as a callback and a .NET Type object which indicates the type of delegate to create for the function pointer. This method returns the required delegate.

3.6 Now for the purposes of the current research work, we will have a pointer to some global function that will serve as a callback when a delegate event is fired. What we need to figure out now is how to acquire a Type object which will be associated with the target delegate. Now the managed Type object is also COM-visible :

[SerializableAttribute]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
[ComVisibleAttribute(true)]
public abstract class Type : MemberInfo, _Type, IReflect

This means that we can use a .NET “Type” object in unmanaged code.

3.7 The first idea I got after researching this far is to create an instance of the .NET Marshal class in unmanaged code, in order to use its GetDelegateForFunctionPointer() method. However, this is not possible bacause the Marshal class is not COM-visible.

3.8 However, it is possible to create a COM-visible managed class that internally uses Marshal.GetDelegateForFunctionPointer() to return a delegate.

3.9 Pursuant to point 3.3 and 3.4 then the solution is to create a small-scale COM-visible wrapper class for the Marshal class as shown below :

[ComVisible(true)]
[Guid("03198B11-4078-4ef2-B745-8FF0DCFDB684")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class MyMarshal
{
    public Delegate GetDelegateForFunctionPointer(IntPtr pUnmanagedFunction, Type type)
    {
        return Marshal.GetDelegateForFunctionPointer(pUnmanagedFunction, type);
    }
}

The MyMarshal class is very simple and exports only one generic method : GetDelegateForFunctionPointer() with the exact same signature as the Marshal.GetDelegateForFunctionPointer() method.

I have included this class as part of the CSTestServer namespace so that when the CSTestServer.dll is compiled, MyMarshal will be part of this class library.

3.10 This class, after being COM-registered, will have the following IDL declaration for its class interface _MyMarshal :

    [
      odl,
      uuid(F6300115-57B3-3637-869C-C2B8D17CDBF1),
      hidden,
      dual,
      nonextensible,
      oleautomation,
        custom({0F21F359-AB84-41E8-9A78-36D110E6D2F9}, "CSTestServer.MyMarshal")    

    ]
    interface _MyMarshal : IDispatch {
        [id(00000000), propget,
            custom({54FC8F55-38DE-4703-9C4E-250351302B1C}, "1")]
        HRESULT ToString([out, retval] BSTR* pRetVal);
        [id(0x60020001)]
        HRESULT Equals(
                        [in] VARIANT obj,
                        [out, retval] VARIANT_BOOL* pRetVal);
        [id(0x60020002)]
        HRESULT GetHashCode([out, retval] long* pRetVal);
        [id(0x60020003)]
        HRESULT GetType([out, retval] _Type** pRetVal);
        [id(0x60020004)]
        HRESULT GetDelegateForFunctionPointer(
                        [in] long pUnmanagedFunction,
                        [in] _Type* type,
                        [out, retval] _Delegate** pRetVal);
    };

3.11 Note the declaration of the GetDelegateForFunctionPointer() method :

        [id(0x60020004)]
        HRESULT GetDelegateForFunctionPointer(
                        [in] long pUnmanagedFunction,
                        [in] _Type* type,
                        [out, retval] _Delegate** pRetVal);

The following are significant points about this method :

  • It takes a long type for the function pointer “pUnmanagedFunction”. This goes inline with the fact that in a 32-bit OS, a function address is generally a 4-byte long value.
  • The “type” parameter is of type “_Type” which is the COM interface counterpart of the managed Type type.
  • The return parameter “pRetVal” is of type “_Delegate” which is the COM interface counterpart of the managed Delegate type.

The “_Type” and “_Delegate” types are available in unmanaged code by referencing the mscorlib.tlb type library.

3.12 I shall provide 2 examples in sections 4 and 5 below (one written in Visual C++ and the other in Visual Basic 6.0) that demonstrate how to hook up a .NET event to an unmanaged global function by using event accessors and delegates. The MyMarshal class will also be used to obtain the CCW of the .NET event delegate.

4. Example Client Code in C++.

4.1 Printed below is a full code listing of a Visual C++ console application that hooks to the TestEvent01 event of an instance of the CSTestServerClass class :

// CSConsoleClient01.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#import <mscorlib.tlb>
using namespace mscorlib;

#import "CSTestServer.tlb" no_implementation raw_interfaces_only
using namespace CSTestServer;

void __stdcall TestEvent01_Delegate()
{
	printf ("TestEvent01_Delegate().\r\n");
}

int _tmain(int argc, _TCHAR* argv[])
{
	::CoInitialize(NULL);

	if (1)
	{
		// --------------------------------------------------------------------------------
		// PART 1 - Instantiation of CSTestServerClass class and obtaining of
		// the Delegate Type for the class's "TestEvent01" event member.
		// --------------------------------------------------------------------------------
		// Create an instance of the managed CSTestServerClass class.
		_CSTestServerClassPtr sp_CSTestServerClass = NULL;

		sp_CSTestServerClass.CreateInstance(__uuidof(CSTestServerClass));

		// Obtain the .NET Type object associated with CSTestServerClass.
		_TypePtr sp_TypeForCSTestServerClass = NULL;
		sp_CSTestServerClass -> GetType(&sp_TypeForCSTestServerClass);

		_TypePtr sp_TypeForTestEvent01 = NULL;

		if (sp_TypeForCSTestServerClass)
		{
		  BSTR bstrEventName = ::SysAllocString(L"TestEvent01");

		  // Obtain the EventInfo object associated with the
		  // TestEvent01 event of the CSTestServerClass class.
		  _EventInfoPtr sp_EventInfoForTestEvent01
		    = sp_TypeForCSTestServerClass -> GetEvent
		      (
		        bstrEventName,
		        (BindingFlags)
		        (BindingFlags::BindingFlags_Instance | BindingFlags::BindingFlags_Public)
		      );

		  ::SysFreeString(bstrEventName);
		  bstrEventName = NULL;

		  // Get the Type object of the underlying event-handler delegate
		  // associated with the TestEvent01 event.
		  // We should be getting the Type object for "CSTestServerClass.TestEvent01_Delegate".
		  sp_TypeForTestEvent01 = sp_EventInfoForTestEvent01 -> GetEventHandlerType();
		}
		// --------------------------------------------------------------------------------

		// --------------------------------------------------------------------------------
		// PART 2 - Instantiation of MyMarshal class and obtaining a Delegate object
		// for our unmanaged global function "TestEvent01_Delegate".
		// --------------------------------------------------------------------------------
		// Create our own Marshal class : MyMarshal.
		_MyMarshalPtr sp_MyMarshal = NULL;

		sp_MyMarshal.CreateInstance(__uuidof(MyMarshal));

		// Use MyMarshal.GetDelegateForFunctionPointer()
		// to obtain a .NET Delegate object of type
		// "CSTestServerClass.TestEvent01_Delegate"
		// and which is associated with the unmanaged
		// function TestEvent01_Delegate().
		_DelegatePtr sp_DelegateForUnmanagedFunction = NULL;

		sp_MyMarshal -> GetDelegateForFunctionPointer
		(
		  (long)TestEvent01_Delegate,
		  sp_TypeForTestEvent01,
		  &sp_DelegateForUnmanagedFunction
		);

		IUnknownPtr spIUnknownForUnmanagedFunction = NULL;
		// Obtain the IUnknown interface pointer of the Delegate.
		if (sp_DelegateForUnmanagedFunction)
		{
			sp_DelegateForUnmanagedFunction -> QueryInterface
			(
			  IID_IUnknown,
			  (void**)&spIUnknownForUnmanagedFunction
			);
		}
		// --------------------------------------------------------------------------------

		// --------------------------------------------------------------------------------
		// PART 3 - Test the Delegate for our global function TestEvent01_Delegate()
		// by using the add_TestEvent01() accessor to hook to the "TestEvent01"
		// event and the remove_TestEvent01() accessor to unhook from the event.
		// --------------------------------------------------------------------------------
		// Use the "TestEvent01" event accessor to add an event
		// handler for "TestEvent01".
		if (spIUnknownForUnmanagedFunction)
		{
		  sp_CSTestServerClass -> add_TestEvent01(spIUnknownForUnmanagedFunction);
		}

		// Test invoke the delegate.
		sp_CSTestServerClass -> TriggerEvent();

		// Use the "TestEvent01" event accessor to remove the event
		// handler for "TestEvent01".
		if (spIUnknownForUnmanagedFunction)
		{
		  sp_CSTestServerClass -> remove_TestEvent01(spIUnknownForUnmanagedFunction);
		}

		// Test invoke the delegate.
		sp_CSTestServerClass -> TriggerEvent();
		// --------------------------------------------------------------------------------
	}

	::CoUninitialize();

	return 0;
}

The following is a summary of the code listing above :

  • The code imports the type library CSTestServer.tlb for the CSTestServer.dll class library which must first be COM registered usingREGASM.EXE.
  • The mscorlib.tlb type library must also be referenced because of the several .NET types that will be used extensively in the code.
  • A global function TestEvent01_Delegate() is defined. This global function will serve as a “delegate” to be invoked when the “TestEvent01” event is raised in the server.
  • The main() function is basically made up of 3 parts.
  • Each of these parts will be explained in more detail in their own sub-sections below.

Part 1

  • The first part consists of the instantiation of the CSTestServerClass class and the obtaining of the Type object associated with the delegate for the class’s TestEvent01 event member.
  • Now, because the CSTestServerClass coclass implements the _Object interface, it will contain the GetType() method.
  • We use this GetType() method to obtain the managed Type object associated with the CSTestServerClass class.
  • We then use this Type object (represented by sp_TypeForCSTestServerClass) to obtain the EventInfo object of the class’s TestEvent01 event (through a call to the GetEvent() method).
  • Once we have obtained this EventInfo object (represented by sp_EventInfoForTestEvent01), we use its GetEventHandlerType() method to obtain the Type object of the underlying event-handler delegate associated with the TestEvent01 event.
  • This will effectively allow us to get the Type object for “CSTestServerClass.TestEvent01_Delegate”.
  • We use the smart pointer sp_TypeForTestEvent01 to hold this Type object.

Part 2

  • The second part consists of the instantiation of MyMarshal class and the obtaining of a delegate object for our unmanaged global function TestEvent01_Delegate().
  • Once an instance of MyMarshal has been created (represented by sp_MyMarshal), we call on the GetDelegateForFunctionPointer() method to obtain a CCW-wrapped Delegate object (represented by sp_DelegateForUnmanagedFunction).
  • A pointer to the TestEvent01_Delegate() global function is passed as the first parameter and the Type object represented by sp_TypeForTestEvent01 is passed as the second parameter.
  • We then query sp_DelegateForUnmanagedFunction for its IUnknown interface pointer.
  • This IUnknown interface pointer will be used later for the call to add_TestEvent01() and remove_TestEvent01().

Part 3

  • The third part involves the testing of the delegate for our global function TestEvent01_Delegate() by using the add_TestEvent01() and the remove_TestEvent01() accessors.
  • We use the pointer to the instance of the CSTestServerClass class (sp_CSTestServerClass) to call the add_TestEvent01() accessor.
  • This adds the delegate for the unmanaged TestEvent01_Delegate() function to the TestEvent01 event.
  • Then, when we call the object’s TriggerEvent() method, the unmanaged global function TestEvent01_Delegate() will be invoked.
  • We then test remove the delegate by calling remove_TestEvent01().
  • When we next call TriggerEvent(), the unmanaged TestEvent01_Delegate() function will not be called.

4.2 Note that if the add_TestEvent01() accessor was called n times, then the unmanaged TestEvent01_Delegate() function will be called n times.

4.3 Consequently, remove_TestEvent01() must be called n times in order to completely remove all calls from the CSTestServerClass object to the unmanaged TestEvent01_Delegate() function.

5. Example Client Code in Visual Basic 6.0

5.1 The second example is written in VB6.0. The way the program sets up a global function as the event delegate to be invoked when the TestEvent01 event was fired by the CSTestServerClass is essentially the same as that of the earlier Visual C++ console application.

5.2 The code consists of a Form object named FormMain and a module named ModuleMain.

5.3 FormMain is a simple form with 3 buttons :

The following is a complete listing of the code for FormMain :

'' For this project, you need to reference the CSTestServer.tlb type library
'' and the mscorlib.tlb type library.
Option Explicit
Dim CSTestServerClassObj As CSTestServerClass
Dim CSTestServerClassType As mscorlib.Type
Dim EventInfoObj As mscorlib.EventInfo
Dim EventType As mscorlib.Type
Dim MyMarshalObj As MyMarshal
Dim DelegateObj As mscorlib.Delegate

Private Sub Command_AddEventHandler_Click()
  '' Use the event accessor to add an event handler
  '' for the "TestEvent01" event.
  CSTestServerClassObj.add_TestEvent01 DelegateObj
End Sub

Private Sub Command_RemoveEventHandler_Click()
  '' Use the event accessor to remove an event handler
  '' for the "TestEvent01" event.
  CSTestServerClassObj.remove_TestEvent01 DelegateObj
End Sub

Private Sub Command_TriggerEvent_Click()
  '' Test trigger the event.
  CSTestServerClassObj.TriggerEvent
End Sub

Private Sub Form_Load()
  '' Instantiate the CSTestServerClass class.
  Set CSTestServerClassObj = New CSTestServerClass

  '' Obtain the .NET Type object associated with
  '' the CSTestServerClass class.
  Set CSTestServerClassType = CSTestServerClassObj.GetType()

  '' Obtain the EventInfo object associated with
  '' the "TestEvent01" event of the CSTestServerClass class.
  Set EventInfoObj = CSTestServerClassType.GetEvent("TestEvent01", _
    (BindingFlags.BindingFlags_Instance Or BindingFlags.BindingFlags_Public))

  '' Obtain the Type object of the underlying event-handler delegate
  '' associated with the "TestEvent01" event.
  '' We should be getting the Type object for "CSTestServerClass.TestEvent01_Delegate".
  Set EventType = EventInfoObj.EventHandlerType

  '' Instantiate our own mini .NET Marshal class.
  Set MyMarshalObj = New MyMarshal

  '' Obtain a .NET Delegate object of type "CSTestServerClass.TestEvent01_Delegate"
  '' which is associated with the unmanaged function ModuleMain.TestEvent01_Delegate().
  Set DelegateObj = MyMarshalObj.GetDelegateForFunctionPointer(AddressOf TestEvent01_Delegate, EventType)

End Sub

The following is a summary of the code above :

  • Alot of work is done in the Form_Load() event handler.
  • Here is where the CSTestServerClass class is instantiated, the Type object associated with the class’s TestEvent01 event is gotten and a delegate object for a global function TestEvent01_Delegate() is obtained through a MyMarshal class instance.
  • Note the use of the AddressOf operator in the call to MyMarshalObj.GetDelegateForFunctionPointer(). This is how a function pointer is obtained in VB6.0.
  • The handler for the “Add Event Handler” button calls on the CSTestServerClass instance’s add_TestEvent01() accessor to add the delegate associated with the TestEvent01_Delegate() global function.
  • The handler for the “Remove Event Handler” button calls on the CSTestServerClass instance’s remove_TestEvent01() accessor to remove the delegate.
  • The handler for the “Trigger Event” button simply calls on the CSTestServerClass instance’s Trigger() method to test fire the “TestEvent01” event.

The following is the code listing for the ModuleMain module :

Public Sub TestEvent01_Delegate()
  MsgBox "TestEvent01_Delegate()"
End Sub

Note well that the AddressOf operator requires a global function which is defined in a module. Hence the sub-routine TestEvent01_Delegate() is defined inside ModuleMain.bas.

5.4 Note that when the application is run, the “Add Event Handler” button must be clicked at least once so that a delegate (associated with the TestEvent01_Delegate() subroutine) is hooked to the CSTestServerClass instance. Then when the “Trigger Event” button is clicked, a Message Box displaying “TestEvent01_Delegate()” will be launched :

5.5 Similar to the earlier VC++ example, if the “Add Event Handler” button is clicked n times, then when the “Trigger Event” button is clicked, the Message Box displaying “TestEvent01_Delegate()” will be launched n times in response to the number of delegates hooked to the event.

5.6 The “Remove Event Handler” button must be clicked n times in order to completely remove all delegates from the event.

6. In Conclusion.

6.1 I certainly have enjoyed the research work which culminated in this report.

6.2 It is truly amazing that the .NET delegate created out of Marshal.GetDelegateForFunctionPointer() can be used as a delegate for hooking events from unmanaged code.

6.3 It shows the de-coupled nature of various components in the .NET world. Kudos to the team at Microsoft.

6.4 I still recommend that events fired from a managed class be hooked to unmanaged code through standard COM interop techniques (by way of ComSourceInterfacesAttribute and COM event sinks).

6.5 But I do hope the research work presented here will be of good use to interested parties.

Advertisements

About Lim Bio Liong

I've been in software development for nearly 20 years specializing in C , COM and C#. It's truly an exicting time we live in, with so much resources at our disposal to gain and share knowledge. I hope my blog will serve a small part in this global knowledge sharing network. For many years now I've been deeply involved with C development work. However since circa 2010, my current work has required me to use more and more on C# with a particular focus on COM interop. I've also written several articles for CodeProject. However, in recent years I've concentrated my time more on helping others in the MSDN forums. Please feel free to leave a comment whenever you have any constructive criticism over any of my blog posts.

Discussion

5 thoughts on “Handling .NET Events in Unmanaged Code using Event Accessors.

  1. Really an exciting piece of work, and exactly what I’d hoped to find (this, and the earlier blog on COM). This knowledge will be put to very good use. And, I should say, this information is simply unavailable eslewhere. I have one book on COM (which I found to be worthless even though written by an author well-known to Microsoft Press habitues) and searched for others on Amazon … I even sent a note off to a well-known publisher: Nothing’s out there.

    Thank you, Sir, for your help with this very difficult topic. It works as advertised.

    Posted by Steve Jensen | October 29, 2012, 4:49 pm
  2. Hi Lim, thanks for posting your insights I really appreciate it!

    I have a problem in running the C++ example code though.

    I get a compiler error (visual c++ compiler) at the following two lines:

    sp_CSTestServerClass -> add_TestEvent01(spIUnknownForUnmanagedFunction);
    sp_CSTestServerClass -> remove_TestEvent01(spIUnknownForUnmanagedFunction);

    which states:
    “no suitable conversion from “IUnknownPtr” to “_TestEvent01_Delegate*” exists.

    Do you have any idea what might cause this and how it might be resolved?

    Posted by Andrew | June 19, 2013, 4:27 pm
    • The problem is resovled through an intermediate cast to void** like this:

      sp_CSTestServerClass -> add_TestEvent01((void**)spIUnknownForUnmanagedFunction);
      sp_CSTestServerClass -> remove_TestEvent01((void**)spIUnknownForUnmanagedFunction);

      Hope this helps.

      Posted by ANREW | June 30, 2013, 7:02 am
      • I have the same problem and after adding that cast I get this error:

        ‘type cast’ : cannot convert from ‘IUnknownPtr’ to ‘void **’

        I also get this error:
        ‘TestEvent01_Delegate’ : ambiguous symbol at this line: (long)TestEvent01_Delegate

        Posted by s3 | December 18, 2013, 11:38 pm
      • Changed the declaration to this to get it to work: _TestEvent01_Delegate * spIUnknownForUnmanagedFunction = NULL;

        Thank you so much for this; it is exactly what I’ve been looking for.

        Posted by s3 | December 19, 2013, 7:53 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: