Managed Calls and Events in Unmanaged C++

Managed Calls and Events in Unmanaged C++

Nowadays, most of the developers and companies want to immigrate to .NET. But, the question still is, how do you implement the new components written in .NET in the old applications: Win32, MFC.... In this article, I will explain how to develop an application in .NET and how to use it even in a simple C++ application.

I hope after that, reading this article, it will be easy for you to navigate between the managed and unmanaged worlds.

Create a "Class Library" project and name it "MyManagedSide".

First of all, add using System.Runtime.InteropServices; to your class to support COM interop. And, to enable a managed code to be used from an unmanaged one, you have to register your .NET application as COM. As you may know, COM must expose some interfaces.

Declare a very simple interface and use a GUID tool to create a GUID in the Registry format.

[Guid("3E0E2EB2-CC13-40fb-9346-34809CB2419C")]
public interface IMyManagedInterface
{
   void SayHello();
}

The interface will be implemented in the MyManaged class. You also create a GUID for the MyManaged-class.

[ClassInterface(ClassInterfaceType.None)]
[Guid("946B4230-33E9-4fd9-8F71-EFBE5E0C2CF2")]
public class MyManaged : IMyManagedInterface
{
   public MyManaged()
   {
   }

   //implementation of the interface method
   public void  SayHello()
   {
      MessageBox.Show("Welcome to the wonderful world of .NET" );
   }
}

[ClassInterface(ClassInterfaceType.None)] indicates that no class interface is generated for the class.

Create a strong name with the command: Sn -k MyManaged.snk and set it in your project settings.

Set the option "Make assembly COM-Visible" in your project settings.

Build your application.

Use the command gacutil.exe" /i MyManagedSide.dll to store your assembly in the GAC. Use the command RegAsm MyManagedSide.dll /tlb:com.MyManagedSide.tlb to register you assembly. After registering your assembly can be used from any COM client. RegAsm is like regsvr32.

MFC client

Create an MFC Project (Dialog based). Select "MFC Class from TypeLib".

Select MyMangedSide from the list and add IMyManagedInterface.

Add CMyDotNetInterface as a member variable to your dialog class and make the following changes:

BOOL CTestComMfcDlg::OnInitDialog()
{
   . . .
   // TODO: Add extra initialization here

   if(!AfxOleInit()) 
   {
      AfxMessageBox(_T("Could not initialize COM services"));
      return FALSE;
   }

   //static const CLSID  clsid =  {0x946b4230,0x33e9,0x4fd9,
   //{0x8f,0x71,0xef,0xbe,0x5e,0x0c,0x2c,0xf2}};
   //m_MyManagedInterface.CreateDispatch(clsid );
   //or
   //lpszProgID = "MyManagedSide.MyManaged"
   m_MyManagedInterface.CreateDispatch(_T
      ("MyManagedSide.MyManaged")));

   //call managed interface
   m_MyManagedInterface.SayHello();

   return TRUE;
}

C++ client

Create an empty console project and make the following changes:

//importing the type library
#import ".. \\com.MyManagedSide.tlb"

void main()
{
   //Initialize the COM library
   CoInitialize(NULL);
   
   //Get the pointer to your .NET Interface and create a new
   //instance
   MyManagedSide::IMyManagedInterfacePtr pMyManagedInterfacePtr;
   HRESULT hRes = pMyManagedInterfacePtr.CreateInstance
      (MyManagedSide::CLSID_MyManaged);

   if (hRes == S_OK)
   {
      //Call your .NET method:
         pMyManagedInterfacePtr->SayHello();
   }

   CoUninitialize ();
}

Managed Calls and Events in Unmanaged C++

Working with events

Now, you will expand your .NET Project to support events. In your project, you add a new interface and we expose it to COM as a dispinterface with a new GUID. You write a method to fire events in the MyManaged-class. Change

[ClassInterface(ClassInterfaceType.None)]

to

[ClassInterface(ClassInterfaceType.None),
   ComSourceInterfaces(typeof(IMyManagedEvent))]
   [Guid("3E35C7C8-BE00-4b02-B33E-7CA88EF8F770"),
      InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
   public interface IMyManagedEvent
   {
      [DispId(1)]
      void MyManagedEvent(long nEventId, string strMessage);
   }

   [ClassInterface(ClassInterfaceType.None),
      ComSourceInterfaces(typeof(IMyManagedEvent))]
   [Guid("946B4230-33E9-4fd9-8F71-EFBE5E0C2CF2")]
   public class MyManaged : IMyManagedInterface
   {
      private long nEventId;

      [ComVisible(false)]
      public delegate void MyEventDelegate(long n, string str);

      public event MyEventDelegate MyManagedEvent;

      public void FireEvents()
      {
         if (this.MyManagedEvent != null)
         {
            this.MyManagedEvent(this.nEventId,
                                DateTime.Now.ToString());
            this.nEventId++;
         }
      }
      ...
   }

MFC client and C++ client

The events notification are supported by two interfaces: IConnectionPointContainer and IConnectionPoint. To set up event notifications in tour client, you will:

  • Implement the connectable object's IDispatch interface in your client.
  • Call QueryInterface on the connectable object to get a pointer to its IConnectionPointContainer interface.
  • Call IConnectionPointContainer::FindConnectionPoint() to obtain a pointer to the IConnectionPoint interface for the events you want to sink.
  • Call IConnectionPoint::Advise() to establish a connection between the connection point object and the client's sink.

[image012.jpg]

You will create a new class derived from the MyManagedSide::IMyManagedEvent. This class is your client's sink. If you don't understand the sink class, only copy this class to your project and substitute the following:

class CMyEventSink : public MyManagedSide::IMyManagedEvent
IsEqualGUID(riid,  __uuidof(MyManagedSide::IMyManagedEventPtr))
#include "myeventsink.h"

void main()
{
   CoInitialize(NULL);
   MyManagedSide::IMyManagedInterfacePtr pMyManagedInterfacePtr;
   DWORD dwEventCookie ;
   CMyEventSink MyEventSink;
   LPCONNECTIONPOINT pIConnectionPoint;

   HRESULT hRes =
      pMyManagedInterfacePtr.CreateInstance
      (MyManagedSide::CLSID_MyManaged);
   if (hRes == S_OK)
   {
      pMyManagedInterfacePtr->SayHello();

      LPCONNECTIONPOINTCONTAINER pIConnectionPointContainer = NULL;
      pIConnectionPoint = NULL;

      //the connection point container
      pMyManagedInterfacePtr->
         QueryInterface(IID_IConnectionPointContainer,
         (LPVOID*)&pIConnectionPointContainer);

      //Returns a pointer to the IConnectionPoint interface of
      //a connection point for a specified IID,
      //if that IID describes a supported outgoing interface.
      HRESULT hresult =
         pIConnectionPointContainer->FindConnectionPoint(
         __uuidof(MyManagedSide::IMyManagedEvent), 
         &pIConnectionPoint); 

      if(!pIConnectionPointContainer)
         pIConnectionPointContainer->Release();

      if (pIConnectionPoint)
      {
         //Establishes a connection between the connection point
         //object and the client's sink.
         HRESULT hr =
            pIConnectionPoint->Advise((LPUNKNOWN)&MyEventSink,
               &dwEventCookie);

         if (SUCCEEDED(hr))
         {
            pMyManagedInterfacePtr->ShowEventDlg();
            pIConnectionPoint->Release();
            pIConnectionPoint->Unadvise(dwEventCookie);
         }
      }

   }

   CoUninitialize ();
}
#pragma once

//importing the type library
#import "...\\com.MyManagedSide.tlb"
   named_guids raw_interfaces_only

class CMyEventSink : public MyManagedSide::IMyManagedEvent
{
public:
ULONG refCount;

CMyEventSink() {
   refCount = 1;
}
~CMyEventSink() {
}

// IUnknown methods.
virtual HRESULT __stdcall QueryInterface(
      REFIID riid, void **ppvObject) {
   if(
      IsEqualGUID(riid,
         __uuidof(MyManagedSide::IMyManagedEventPtr)) ||
      IsEqualGUID(riid, IID_IUnknown)
   ) {
      this->AddRef();
      *ppvObject = this;
      return S_OK;
   }
   *ppvObject = NULL;
   return E_NOINTERFACE;
}

virtual ULONG _stdcall AddRef(void) {
   return ++refCount;
}

virtual ULONG _stdcall Release(void) {
   if(--refCount <= 0) {
      //Delete this;
      return 0;
   }
   return refCount;
}

// IDispatch methods.
virtual HRESULT _stdcall GetTypeInfoCount(UINT *pctinfo) {
   if(pctinfo) *pctinfo = 0;
   return E_NOTIMPL;
}

virtual HRESULT _stdcall GetTypeInfo(
      UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) {
   return E_NOTIMPL;
}

virtual HRESULT _stdcall GetIDsOfNames(
      REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid,
      DISPID *rgDispId) {
   return E_NOTIMPL;
}

virtual HRESULT _stdcall Invoke(
      DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
      DISPPARAMS *pDispParams, VARIANT *pVarResult,
      EXCEPINFO *pExcepInfo, UINT *puArgErr) 
{

   switch(dispIdMember)
   {
      case 1L:
        {
            //Event parameters
            long lEventId = pDispParams->rgvarg[1].lVal;
            _bstr_t strMsg = pDispParams->rgvarg[0].bstrVal;


            OnMyNetEvent(lEventId, strMsg);
            break;
        }
   }
   return S_OK;
}

void OnMyNetEvent(long lEventId, _bstr_t bstr1)
{
    //do something
}

};


About the Author

Safouane Hicham

I am graduate engineer and I have a diploma in physic. I am working for a leader in laser-based fabrication. My expertise area: Win32, MCF, .Net, Java, Oracle, Microsoft SQL Server, image and signal processing

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

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

Most Popular Programming Stories

More for Developers

RSS Feeds