Invoking .NET Events from Native C++

Introduction

When writing .NET wrapper classes to interface with native C++ code, there are occasions when the native code will need to raise events in the .NET wrapper class. Examples of this are either virtual overrides (for example, OnReceive and OnClose in the CAsyncSocket class) or message mapped functions on CWnd-derived classes (as in OnSize, OnMove, and so forth).

However, .NET only allows events to be raised from inside of the containing class. To do what we want, we must have a native C++ object that invokes the appropriate event via a public member function on the managed wrapper class.

The following diagram shows a virtually overridden function invoking an event on a .NET class through a public member function.

However, this isn't good design. We don't want a class anywhere in the system to be able to raise these events. We want only our native class to be able to do it. Never mind the fact it clutters up the class definition needlessly.

In native C++, the way around such issues was to use 'friends'—one class could call protected and private members on another. But .NET doesn't allow friends. So what is the solution to our problem?

Solution

The solution is to have an intermediate managed class existing in between the native class and the .NET class with the same events as the .NET class.

Thus, in code terms we have:

class CNative
{
protected:
   virtual void OnEvent();
} ;

__gc class NativeInterface;
class CNativeDerived : public CNative
{
public:
   CNativeDerived(NativeInterface *pInterface)
   {
      m_pInterface = pInterface;
   }

protected:
   virtual void OnEvent()
   {
      m_pInterface->RaiseEvent();
   }

private:
   gcroot<NativeInterface *> m_pInterface;
} ;

__gc class NativeInterface
{
public:
   NativeInterface()
   {
      m_pDerivedClass = new CNativeDerived(this);		
   }

   virtual ~NativeInterface()
   {
      delete m_pDerivedClass;
   }

   __event System::EventHandler *Event;

   void RaiseEvent()
   {
      if (Event != NULL)
      {
         Event(this, NULL);
      }
   }

private:
   CNativeDerived *m_pDerivedClass;
} ;

public __gc class ManagedWrapper
{
public:
   ManagedWrapper()
   {
      m_pInterface = new NativeInterface;
      m_pInterface->Event += new EventHandler(this, OnEvent);
   }

   __event System::EventHandler *Event;

protected:
   virtual void OnEvent(System::Object *object, System::EventArgs *args)
   {
      if (Event != NULL)
      {
         Event(this, null);
      }
   }

private:
   NativeInterface *m_pInterface;
} ;

Therefore, if this functionality is put into an assembly in a DLL, only the ManagedWrapper class and its event will be visible.

Example

The example demonstrates one method of inter-thread communications in native code—using windows messages—and provides this functionality to .NET projects. It consists of a C++.NET assembly that contains a CWnd-derived class (CMessengerWindow) that posts a message to itself to signal one thread from another.

There is a managed interface class (MessengerInterface) that has a 'RaiseMessage' member function that is called by the window when it receives its signal message. This in turn raises its event, which is processed by the visible wrapper class (Messenger) causing it to raise its own event to signal clients.

The advantage of using this method is that each thread never explicitly calls methods on the other, removing the need for synchronisation. As an example of use, a C# project is included; it creates an instance of the Messenger on the main thread and starts a seperate thread to send messages to the main thread.

There is a message list class that the seperate thread uses to send communications to the main thread. The thread directly calls the main thread as well as going through the Messenger, and information about the thread ID is displayed in the list box.

As can be seen, when called through the messenger the method is always executed on the main thread. Obviously, this method assumes that the thread on which the Messenger class is created contains a message loop: This method, for instance, won't work in a console application.

Another useful item in the C# project is how to terminate threads cleanly. Calling Abort() on a thread will cause it to exit at the first available opportunity—which means as a developer you don't know on which statement in the thread function the thread will exit. The thread in the example waits on an event to inform it when to exit. This is a safer way of exiting the thread because you can be sure that all the thread functions have completed before the thread exist.

Conclusion

We have seen how to avoid the lack of 'friends' in .NET when processing messages from native classes. We have also seen an example of use demonstrating how to communicate between threads in a thread-safe manner.



About the Author

David McClarnon

He first encountered Windows programming using Visual C++/MFC version 1.5 on Windows 3.11 a very long time ago. He is now a contract developer specialising in .NET/native interop with p/invoke.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Where the business performance of their mobile app portfolios are concerned, most companies are flying blind. While traditional application portfolios are held to all kinds of ROI measure, the investment plan for mobile apps -- increasingly the more crucial bet -- is made by guesswork and dart-throwing. This interactive e-book investigates how mobile is driving the need for app and portfolio measures unlike any we saw in the days of web. Good mobile analytics must deliver leading indicators of user experience …

  • Live Event Date: July 30, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT You may already know about some of the benefits of Bluemix, IBM's open platform for developing and deploying mobile and web applications. Check out this upcoming eSeminar that focuses on building an Android application using the MobileData service, with a walk-through of the real process and workflow used to build and link the MobileData service within your application. Join IBM's subject matter experts as they show you the way to build a base …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds