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

  • When individual departments procure cloud service for their own use, they usually don't consider the hazardous organization-wide implications. Read this paper to learn best practices for setting up an internal, IT-based cloud brokerage function that service the entire organization. Find out how this approach enables you to retain top-down visibility and control of network security and manage the impact of cloud traffic on your WAN.

  • U.S. companies are desperately trying to recruit and hire skilled software engineers and developers, but there is simply not enough quality talent to go around. Tiempo Development is a nearshore software development company. Our headquarters are in AZ, but we are a pioneer and leader in outsourcing to Mexico, based on our three software development centers there. We have a proven process and we are experts at providing our customers with powerful solutions. We transform ideas into reality.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date