Serengeti logo BLACK white bg w slogan
Menu

MFC and .NET Interoperability

Igor Marković, Software development consultant
24.03.2020.

MFC (Microsoft Foundation Classes) is Microsoft's library for creating Win32 applications using C++. It is a set of C++ wrapper classes centered around the Win32 API. In each MFC project, corresponding MFC source code files are referenced.

The first version was released in 1992. Although it has long been succeeded by the .NET environment as the most widely-used Windows development framework, MFC is by no means ‘dead’. It is likely to be found in some older legacy code bases, and it’s still being developed and supported by Microsoft.

This should serve as a short intro to the somewhat archaic MFC library. Nowadays, the .NET framework is much more widely used. Nevertheless, you might come across the need to connect an old MFC application to a new one written in .NET, for example, if the MFC one is too large and complex for easy migration to .NET. So, let's take a look at how to do that.

A prerequisite to using this approach is the existence of a legacy MFC application still used in production, in parallel with newer applications written in .NET. At some point the need is recognized to connect the old application to a part of the user interface or code written in .NET.

In this example, we will create a very simple MFC desktop application using the built-in Visual Studio wizard. We won’t cover the details on how to do this in this post. In real life, instead of this simple app there would be a very large and complex one, the abandoning of which would be a big no-no J.  This simple app will allow the possibility of sending data to the .NET code, as well as receiving data from it. In this example, we will send and receive text (strings) and integers.

We will add one simple dialog to the app, making the UI look like this:

The MFC dialog consists of:

  • A textbox for entering some text
  • A button for calling the .NET code and forwarding the entered text to it
  • Two text boxes showing the received data: dialog result (int) and received answer (string)

The mentioned .NET code in this case is a WPF dialog, which looks like this:

After clicking OK, the previously shown MFC dialog will look like this:

So, now let’s look under the hood of this, from the user’s point of view, quite simple app.

Structure

The following parts are required:

  1. An MFC application – the starting point
  2. A .NET application; in this example, a WPF application.
  3. A ‘proxy’ .NET library
  4. A ‘proxy’ MFC library, which connects the two ‘worlds’ – MFC and .NET

Parts 2 and 3 can be merged into one, but for the sake of the Separation of Concerns (SoC) concept, let’s keep them apart.

We will add the corresponding projects to Visual Studio solution:

  • MFCDemo (part 1)
  • WpfDemo (part 2)
  • WpfLib (part 3)
  • Mfc2Wpf (part 4)

A graphical representation of the respective project references looks like this:

Now, let’s look at an overview of each of the parts (Visual Studio projects).

.NET application

This is by far the simplest part. The concept is shown in the following image:

There is a window with user control. This user control is shared between .NET and indirectly WPF, via a .NET proxy library.

.NET proxy

The concern of this part is to provide a special dialog that can be called from the MFC code, but whose UI is in .NET. The concept is shown in the following image – similar to the situation in the .NET application:

WpfWindow is just like any window in WPF with one small addition. It uses the _fpresent() method:

[DllImport("msvcr70.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int _fpreset();

This method is called in the constructor:

public WpfWindow()
{
     _fpreset();
     InitializeComponent();
}

The purpose of using this function is to avoid potential errors if you’re using floating point data.

In this library, there is also a proxy class which is called from the C++ code. It is important that it receives a window handle number from the calling C++ application, in order to display the dialog correctly. Therefore, there is the corresponding property:

public int HwndMainWnd { get; set; }

Before the dialog is opened, a window handle should be set:

WpfWindow window = new WpfWindow();
if (HwndMainWnd != 0)
{
      WindowInteropHelper helper = new WindowInteropHelper(window);
      helper.Owner = (IntPtr)HwndMainWnd;
}

MFC proxy

And here comes the show!

This is the crucial part, which connects MFC and .NET. It is an MFC class library, but it must have the support for Common Language Runtime. Therefore, the corresponding setting has to be set:

This enables the usage of .NET code in C++.

First, in C++ there must be a header file with necessary declarations. Here are the contents of MfcProxy.h, the header file of MfcProxy class which is used for implementation of the interoperability:

class __declspec(dllexport) MfcProxy
{
       public:
             MfcProxy(); //constructor
             int DoModalWpf(); //the method which is called
             CString msg; //received message
             CString answer; //answer to send
             HWND hwndMainWnd; //window handle
};

Now let’s have a step-by-step overview of the implementation.

First, there have to be the necessary includes – for the C++ part, as well as the usings – for the .NET part:

#include "stdafx.h"
#include "MfcProxy.h"
using namespace System;
using System::Runtime::InteropServices::GCHandle;
using System::Runtime::InteropServices::Marshal; 
using namespace WpfLib;

The constructor is simple:

MfcProxy::MfcProxy()
{
       msg = "";
       hwndMainWnd = NULL;
}

The most important part is the method which actually calls the .NET window:

int MfcProxy::DoModalWpf()

First, the new object has to be instantiated. But not the C++ object, the .NET one. Therefore, instead of a new operator, another operator has to be used – gcnew:

//create a new instance of a managed object - gcnew
WpfDialogProxy ^ wpfDialogProxy = gcnew WpfDialogProxy();

Next, the message to the dialog has to be sent (set). It’s not as simple as it may look at the first glance. The point is that the received unmanaged string has to be converted to a managed string in order to be usable in .NET. For that purpose, the Marshal library is used:

//convert unamaged string to managed string, set the received message
wpfDialogProxy->ReceivedMsg = Marshal::PtrToStringAnsi((IntPtr) msg.GetBuffer());

 The parent window handle has to be set so that the Windows UI knows to which parent window the calling dialog belongs:

//set parent window handle
if (hwndMainWnd != NULL)
{
wpfDialogProxy->HwndMainWnd = (INT_PTR)hwndMainWnd;
}

Now everything is prepared to finally call the dialog:

//call modal dialog
int retVal = 0;
retVal = wpfDialogProxy->DoModal();

After calling the dialog, the answer message has to be received. The process is the opposite to the already-mentioned process of sending the message:

//get the answer
//convert managed string to int ptr
IntPtr intPtr = Marshal::StringToHGlobalAnsi(wpfDialogProxy->SentMsg);
//convert int ptr to CString
answer = (CString) (char *) intPtr.ToPointer();

In the end, the used managed resources have to be freed, and the return value can be returned:

//free resources
Marshal::FreeHGlobal(intPtr); 
return retVal;

MFC application

There are a few things that need to be mentioned regarding the main project, apart from creating it from a wizard template.

Because string messages are sent and received, in order to ensure their correct interpretation, the following setting has to be set:

In order to avoid issues with .NET threading, the following line has to be added to the InitInstance method of the MFC application:

// CMFCDemoApp initialization
BOOL CMFCDemoApp::InitInstance()
{
       CWinApp::InitInstance();
       CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

The logic in the MFC dialog is quite simple:

void Dialog1::OnBnClickedButton1()
{
       CString txt;
       //get text
       GetDlgItemText(IDC_EC1, txt);
       //declare the proxy object
       MfcProxy mfcProxy; 
       //set the message
       mfcProxy.msg = txt;
       //set window handle
       mfcProxy.hwndMainWnd = this->GetSafeHwnd();
       //open dialog
       INT_PTR nDlgResult = mfcProxy.DoModalWpf();
       //process response
       if (nDlgResult == IDOK)
             SetDlgItemText(IDC_EDIT2, (LPCSTR) "OK");
       else
             SetDlgItemText(IDC_EDIT2, (LPCSTR) "Cancel");
       SetDlgItemText(IDC_EDIT3, (LPCSTR)mfcProxy.answer);
}

Wrapping Up

MFC is a much more complex framework than .NET. It is also more directly connected to lower-level mechanisms of Windows. It has been in wide use for a relatively long time now, so there is a real possibility of coming across legacy code written in it. It is also possible that such code should be connected with a newer .NET code.

In this post, one possible approach for implementing interoperability is demonstrated. There are other possibilities, of course. You may even derive your own approach from this one.

For further and more detailed referencing, you can download the source code here.

Let's do business

The project was co-financed by the European Union from the European Regional Development Fund. The content of the site is the sole responsibility of Serengeti ltd.
cross