This article demonstrates the concept of how to build an old MFC application in a .NET application, to replace the MFC MDI Framework with the modern WinForms or the WPF Framework and to integrate MFC Views and Dialogs in it.
Programming Guide with source code for hosting simple wizards MFC/Dialog application:
Contents
As far as the way in which .NET has been developed, for a bunch of applications made in the MFC MDI architecture, there is a new problem of expensive migration into .NET. I've found only one migration tool - RC Converter by DudeLabs. But it supports the transformation of a small part of MFC MDI GUI - MFC resources only. The larger part of the GUI and all business logic, you have to convert yourself.
As an alternate to the migration scenario, Microsoft offers some interoperation solutions (in an interoperation scenario, you don't try to turn the MFC code into .NET automatically, you just try to ensure that they can work together):
The first technology allows to host the MFC control only. ("If your Win32 logic is already packaged up nicely as a control...") You can turn a dialog into a control with some efforts - but not the larger MFC MDI application.
The MFC Windows Forms support classes allow the backwards integration of the .NET code in an MFC application. It lets you arrange .NET Forms as Views or Dialogs. There are some important limitations in this case: the .NET Framework cannot be integrated (the old MFC Framework has to be used), the part of the integrated Forms functionality is not available (for example, laying out), and a separate control can be integrated as a Form only (for example, the .NET MenuStrip
cannot be used in the MFC Framework). I find that these kinds of interoperation solutions are pure alternates to the migration scenario, and I haven't found real examples of these solutions on the Internet.
In view of a specific application (UMLEditor - revisiting the vector editor by Johan Rosengren), this article illustrates how you can build your old MFC MDI application in an existing .NET application or rapidly create a new WinForms/WPF GUI Framework for it. After this, you can gradually migrate from the old MFC dialogs and views to the new WinForms and then to the WPF UI elements. Such an intermediate integration of MFC and WinForms might seem to be more expedient for larger MFC MDI projects in comparison with a complete migration into .NET.
From the technical point of view, a mixed MFC DLL with a CWinApp
-derived class, the hosting of MFC views in WinForms controls, and the support of the WPF auto-layout in hosted .NET 1.x and .NET 2.0 WinForms controls used in this article, are not discussed on the Internet.
It is an example of how your application's GUI framework can be evolved by using the MfcAdapter
wrapper (50 KB! source code for the WinForms and WPF version):
UMLEditor
as an original MFC MDI application:
UMLEditor
built using the WinForms Framework - Forms with docking and skinning (stardock):
UMLEditor
built in a WPF page (FlowDocument
with two UML diagrams):
As a rule, the MFC class based application architecture is strongly typed. A lot of classes, for example, the MDI classes CDocument
, CView
, CFrameWnd
, and others cannot be used without the CWinApp
(application) class and a complicated initialization. The simple solution in this case is to put the full application in a DLL and to mix with the managed C++ classes. We can use these classes as the managed interface between the WinForms Framework and the MFC application. In this case, we have to resolve the problem with a "second" MFC application main window. It has to be invisible, and all the MFC windows have to be hosted in a WinForms MainForm window. However, all the functionality of the MFC application can be used in a managed Framework in this case. First of all, let's discuss mixed DLL creation. We have different solutions for VS2003 and VS2005 as the mixed DLL loading process is different in each case (Initialization of Mixed Assemblies).
Let's use a standard solution from MSDN (Converting Managed Extensions for C++ Projects from Pure Intermediate Language to Mixed Mode). See the paragraph "To modify your DLL that contains consumers that use the managed code and DLL exports or managed entry points."
In order to prevent calling unsafe code during DLL initialization (loader lock problem), Microsoft offers to use the /noentry linker option. In this case, the mixed DLL has no entry point, _CRT_INIT
doesn't call the static
MFC constructors, and _DllMainCRTStartup
doesn't call the MFC DllMain
(which calls CWinApp::InitInstance
). Only managed objects would be created and initialized.
To create static
MFC objects and to initialize MFC objects, you have to call the __crt_dll_initialize()
static
function from your managed code. You have to be careful about the place to call __crt_dll_initialize()
, and be careful with your MFC constructors and the InitInstance
method code - don't forget about the loader lock problem, and don't use un-initialized MFC native code!!!
Besides this, you have to explicitly call InitApplication
at the beginning of InitInstance
instead of calling CWinApp::InitInstance()
. Otherwise, that will cause a problem with CDocManager
, PreTranslateMessage
(translation of the short keys into command messages), memory leaks, and others.
There are some general notes on the initialization of mixed MFC and managed C++ DLLs. I won't describe the special cases of MFC object initialization (for example, the initialization of an extension DLL's resource chain), because that is not the main theme of this article. The last note is about termination. You have to be careful to close your CWinApp
object first (see the example code in MfcAppAdapter.cpp) and then make a call to __crt_dll_terminate()
. The WinForms Framework would be closed properly in this case.
Now, we can check this solution with the UMLEditor
(or with the simple wizards MFC/Dialog application). In the case of the UMLEditor
, the following project configuration changes were made:
Configuration Type | Dynamic Library (.dll) |
Character Set | Use Unicode Character Set |
Use Managed Extensions | Yes |
Output File | .dll |
Additional Options | /noentry |
Update options which come against the compiler option /clr for the project configuration file and for other source files.
Basic Runtime Checks | Default |
Create the MfcAppAdapter.cpp file, as shown in the picture below, with a simple managed class. The use of the "Singleton" pattern allows us to encapsulate initialization and the disposing of this class from the WinForms code. It enables the MFC application to initialize before its first use, and terminate the MFC application before the WinForms application exits. Besides this, the MfcAppAdapter
enables wrapping of the MFC command message interface. We use this class only for DLL testing (more safety code is in the download):
#include "stdafx.h"
#include "_vcclrit.h"
using namespace System;
using namespace System::Windows::Forms;
public __gc class MfcAppAdapter
{
private:
static MfcAppAdapter* m_Instance;
MfcAppAdapter()
{
__crt_dll_initialize();
Application::add_ApplicationExit(
new EventHandler(this,
&MfcAppAdapter::OnApplicationExit));
}
void OnApplicationExit(Object* sender, EventArgs* e)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CMDIFrameWnd* pMainFrame =
dynamic_cast <CMDIFrameWnd*>(AfxGetMainWnd());
::SendMessage(pMainFrame->GetSafeHwnd(),WM_CLOSE,0,0);
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_bDLL = NULL;
__crt_dll_terminate();
m_Instance=NULL;
}
public:
static MfcAppAdapter* GetInstance()
{
if(m_Instance==NULL)
{
m_Instance = new MfcAppAdapter();
}
return m_Instance;
}
bool OnCmdMsg( int nID)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CMDIFrameWnd* pMainFrame =
dynamic_cast <CMDIFrameWnd*>(AfxGetMainWnd());
return pMainFrame->OnCmdMsg(nID, 0, NULL, NULL) !=
FALSE;
}
};
The UMLEditor
doesn't need special initialization, so we only add InitApplication()
to UMLEditorDemo::InitInstance
in UMLEditorDemo.cpp. (For other applications, some small changes are necessary.)
After building the DLL, we can host and test it in a simple C# WinForms application (see the picture):
public Form1()
{
...
private void button1_Click(object sender,
System.EventArgs e)
{
MfcAppAdapter.GetInstance().OnCmdMsg(0xE140);
}
}
We send the ID_APP_ABOUT
command message to the UMLEditor
using a button click:
As we can see above, only managed initialization is performed in VS2003. We make an unmanaged initialization ourselves after a managed initialization. In VS2005, both unmanaged and managed initializations are performed - the unmanaged initialization takes place first, and the managed initialization takes place after that. Loader lock can still occur (for example, if the code within DllMain
accesses the CLR), but now it occurs deterministically, and is detected. Don't try to skip the unmanaged initialization with a /noentry linker option and call __crt_dll_initialize()
in VS2005!!!
To guarantee that the code within DllMain
and the static
variable constructors don't access the CLR, the new mixed DLL loading process needs a special project configuration. No function in the call tree rooted at DllMain
can be compiled to MSIL, because it means access to not initialized CLR. We can resolve the issues here, the object file containing these functions should be compiled without /clr. In our case, we set "No Common Language Runtime support" in project properties and "Common Language Runtime Support (/clr)" in every file with the managed classes' properties. If DllMain
attempts to execute the MSIL directly, it will result in a compiler warning (level 1) C4747. However, the compiler cannot detect cases where DllMain
calls a function in another module, which in turn attempts to execute the MSIL. Static
variables with MSIL instructions in the initialization code have to be placed in the managed modules, and should not be initialized and used by unmanaged initialization.
Then, we have to resolve a linker problem - the LNK2005 error. Microsoft does not support CWinApp
derived classes in a MFC extension DLL - so may be it is not a linker bug. In any case, if DllMain
from the CRT library is linked before the DllMain
from the MFC libraries, there will be a link error: "mfcs80ud.lib(dllmodul.obj) : error LNK2005: _DllMain@12 already defined in msvcrtd.lib(dllmain.obj)". I resolve this problem with a small trick: add macro AFX_MANAGE_STATE (AfxGetStaticModuleState())
to the static
constructor or at the beginning of InitInstance
.
The problem with InitApplication
has the same solution as in VS2003.
We don't need to call a special function to create and initialize the static MFC objects, and we don't need to call the termination function! But we have to close the MFC application (use the same code as in VS2003).
In contrast to VS2003, if the MFC application makes an activation of the MainFrame window after the WinForms Main Form activation, then the Managed Debugging Assistants throw the LoaderLock
exception. But as will be discussed later, we don't need two activated main windows, so we don't activate the MFC MainFrame window.
We write all the managed classes from the mixed DLL in the new C++/CLI. Use "Managed Extensions for C++ Syntax Upgrade Checklist" for migrating managed VC++ to C++/CLI, or use VS2003 MC++. In this case, simply set the project option /clr:oldSyntax instead of /clr. All other project settings would be the same as for C++/CLI.
Now, we can check this solution with the UMLEditor
(or with the simple wizards MFC/Dialog application).
After auto-updating the UMLEditor
in VS2005, make the following changes in the project configuration:
Configuration Type | Dynamic Library (.dll) |
Character Set | Use Unicode Character Set |
Use Managed Extensions | No |
Output File | .dll |
Update options which come against the compiler option /clr for the project configuration file and for other source files:
Basic Runtime Checks | Default |
Enable Minimal Rebuild | No |
Enable C++ Exceptions | With SHE Exceptions (/EHa) |
Debug Information Format | Program Database (/Zi) |
Create/Use Precompiled Headers | Not Using Precompiled Headers |
Entry Point | |
Create the MfcAppAdapter.cpp file, from the picture below, with a simple managed class. We removed __crt_dll_initialize()
, __crt_dll_terminate()
from the VS2003 version and used the new C++/CLI syntax. Set CLR support in the MfcAppAdapter.cpp file property:
Compile with Common Language Runtime support | Common Language Runtime Support (/clr) |
#include "stdafx.h"
using namespace System;
using namespace System::Windows::Forms;
public ref class MfcAppAdapter
{
private:
static MfcAppAdapter^ m_Instance;
MfcAppAdapter()
{
Application::ApplicationExit +=
gcnew EventHandler(this,
&MfcAppAdapter::OnApplicationExit);
}
void OnApplicationExit(Object^ sender, EventArgs^ e)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CMDIFrameWnd* pMainFrame =
dynamic_cast <CMDIFrameWnd*>(AfxGetMainWnd());
::SendMessage(pMainFrame->GetSafeHwnd(),WM_CLOSE,0,0);
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
pModuleState->m_bDLL = NULL;
delete m_Instance;
m_Instance=nullptr;
}
public:
static MfcAppAdapter^ GetInstance()
{
if(m_Instance==nullptr)
{
m_Instance = gcnew MfcAppAdapter();
}
return m_Instance;
}
bool OnCmdMsg( int nID)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
CMDIFrameWnd* pMainFrame =
dynamic_cast <CMDIFrameWnd*>(AfxGetMainWnd());
return pMainFrame->OnCmdMsg(nID, 0, NULL, NULL) != FALSE;
}
};
Add InitApplication()
to UMLEditorDemo::InitInstance
in UMLEditorDemo.cpp. Build the DLL and test it in a simple WinForms 2005 application.
Where are we? So far, we have discussed only the technical problems involved with creating mixed MFC with a CWinApp
-derived class and a managed C++ DLL. But, how can we host MFC windows in WinForms or WPF Framework windows? Let's discuss the architecture of the MfcAdapter
wrapper:
The MfcAdapter
is a managed component that consists of MfcAppAdapter
and a suitable WndManager
:
MfcAppAdapter
(C++/CLI) is a wrapper to an unmanaged legacy MFC application. This is the kern of the MfcHost
. The general task involves the initialization of the encapsulated MFC application, the command interface to this application, and its windows hosting. The command interface means an interface to the command handlers in the legacy MFC application. To access the Framework, first of all, for attaching the MFC windows to the Framework, MfcAppAdapter
uses an external IWndManager
interface. This solution allows us to use only one MfcAppAdapter
for any Framework type. MfcAppAdapter
classes use only MFC based classes of the legacy MFC application, so it's independent of the specific classes of UMLEditor
and can be used for wrapping other applications. WndManager
is a facade to the Framework which allows us to realize MfcAppAdapter
as a component of the used Framework - WinForms component for the MDI Forms Demo, or WPF FrameworkElement
for the WPF Page Demo. For other managed Frameworks, suitable WndManager
s can be implemented. The common part of WndManager
s is the IWndManager
interface. WndManager
implements this interface for the specific Framework. The remaining part of WndManager
depends on the Framework and on the MFC application functionality that we need. For example, the MdiFormsWndManager
realizes the handling and update of the Framework's ToolStripMenuItems
and ToolStripButtons
(MenuItem
s and ToolBarButton
s for VS2003) in the MFC application, and supports the Visual Studio Designer in design-time. WpfPageWndManager
realizes the OpenDocument
method, and supports the WPF Autolayout manager. In this article, we will discuss MfcAppAdapter
and WpfPageWndManager
. MdiFormsWndManager
is supplementary, in our case, and won't be discussed in this article, but you can use the source code.
We discuss here the most important issues of the MfcAppAdapter
implementation. Use the source code for a detailed understanding.
Before we can use the created mixed DLL, we have to resolve some problems.
Unicode Set
Unicode Character Set to simplify string message marshalling.
Hosting Dialogs, Creation, and Activation of the MFC Main Window (UmlEditorDemo.cpp)
MFC dialog windows use the main window of the legacy MFC application as the default parent window. We can keep the modal properties of the hosted dialogs, if we set the WinForms Framework MainForm window as a parent for the legacy MFC application main window. We can resolve this with the following solution:
- Add the
m_ExtFramework
member, AttachApplication
method, and the AttachApplication
static function to our application class. m_ExtFramework
saves the interface to the WinForms Framework (IFramework
). AttachApplication
method is a setter for m_ExtFramework
, and the AttachApplication
static
function is a simple interface to access the AttachApplication
method (we keep the wrapper classes MfcAppAdapter
and ViewCtrl
independent of the MFC application). The WinForms Framework calls the AttachApplication
method after a mixed DLL initialization but before using any of the legacy MFC applications. - Remove the creation of the main window from
InitInstance
to the AttachApplication
method. - Override
CMainFrame::PreCreateWindow
, and set the MainForm window handle from IFramework
in cs.hwndParent
. - By closing the MainForm window, the child's windows are destroyed. The legacy main window receives the
WM_DESTROY
message. We have to be careful to close the legacy MFC application in this case. But the normal close scenario occurs when a client calls dispose on the MfcAppAdapter
and closes the legacy MFC application before the WinForms Framework MainForm is closed. So, we write a warning to the output window if the client forgets this. - The message loop implementation is different for .NET 1.x and .NET 2.0. In .NET 1.x, the message loop tries to preprocess some common control messages (
WM_NOTIFY
, WM_COMMAND
) from the modeless dialog controls, first of all, in the MainForm
. We resolve this problem by explicitly pre-translating these messages in the legacy MFC application.
We also use CNotifyHook
as the COleFrameHook
interface realization, to enable the WinForms Framework windows to synchronize with the legacy MFC application windows.
We keep the legacy MFC application main window invisible. So, some source code has to be removed from the application InitInstance
:
The command line processing is in the WinForms Framework now. So, remove this code too:
Hosting View
For hosting MFC views in WinForms controls, we have to change the view classes and the dependent classes, as well:
- First of all, the view's content has to be invisible when creating and initializing. It would be activated only after attaching to the WinForms control. In the source code, we set
bMakeVisibleparameter
to false
in the overridden MultiDocTemplate::OpenDocumentFile
, CDocManagerEx::OpenDocumentFile
, and CMainFrame::OnWindowNew
. - We have to save the pointer to the last opened document, which we will use in the WinForms control. It's realized by the
SetOpenedDocument
method of our IFramework
interface. - Don't use
CView::OnCreate
. Creating and initializing of our view class' members inherited from CWnd
, Control
, DropTarget.Register
, ToolTip
, and so on, should be removed from the OnCreate
method to the OnInitialUpdate
. In this case, these members will be created after the view is attached to the WinForms control. - After the view is attached to the WinForms control by the
WndManager
, the CView::GetParent
method will return the parent hWnd
of the WinForms control. So, the entire code uses the CView::GetParent
; for example SetTitle
, has to be changed to the GetNativeParent
method of our IFramework
. We make this for CDocManagerEx::OpenDocumentFile
, CUMLEditorDemoDoc::CanCloseFrame
, and CUMLEditorDemoDoc::UpdateFrameCounts
. By closing the control container, WndManager
has to dispose the control with the attached view!!! In this case, the view would be detached, the MFC CFrame
parent of the view would be restored, and then the view would be closed in the MFC application. We have to check that all views are detached from the WinForms controls before we close the MFC application. UMLEditorDemo::DetachApplication
method does this check. - For auto-layout support, we have to implement some methods in our hosted view:
AutoSize
, GetPreferredSize
, and ScaleControl
. We use the base class CLayoutView
with these methods as the interface for the MfcAppAdapter
from the hosted view. The view has to inherit this class. All custom properties of the view which are used in the control have to be implemented in this fashion. - In this article, we don't describe the changes of the view classes inherited from the controls and views with
m_dropTarget
(support of drag & drop), because the UmlEditor
does not have these views.
MfcAppAdapter
The command interface of the legacy MFC application is wrapped by two methods: OnCmdMsg
(CCmdTarget::OnCmdMsg
) and OnUpdateCmdMsg
. We also use the class CCmdUIHandler
for the user interface update. This class realizes the CCmdUI
interface for the framework menu items and toolbar buttons. The GetMfcMenuString
method realizes the access to the MFC CMenu
to support "Recent" menu items in the WinForms menu.
In the VS2003 version, the TranslateMsg
method supports message dispatching for common controls.
MfcAppAdapter
is a singleton. The method CreateInstance
can be used to access a private static
instance. The MfcAppAdapter
constructor calls UMLEditorDemo::AttachApplication
to initialize the legacy MFC application. The MfcAppAdapter
's Dispose
checks if all the hosted views are closed, and then closes the legacy application with the UMLEditorDemo::DetachApplication
method.
ViewCtrl
ViewCtrl
supports MFC view hosting in WinForms controls. It is a helper class of MfcAppAdapter
.
- As a child of a WinForms control,
ViewCtrl
inherits the Control
interface. For a view hosting, we have to detach the CView
object from the "CView
" window and attach the CView
object to the "ViewCtrl
" window by creating a "ViewCtrl
" window. - We keep dispatching messages from the "
CView
" window to the CView
object, by replacing CViewWndProc
with StubWndProc
. StubWndProc
dispatches these messages from the "CView
" window to the "ViewCtrl
" window and then to the CView
object. - To keep the
CWnd
interface for the "CView
" window, we have to attach a StubWnd
(CWnd
based) to the "CView
" window. Now, our CView
object is connected with the "ViewCtrl
" window and with the "CView
" window! It's not so bad. See the picture:
- We can make these operations by creating the "
ViewCtrl
" window, if we change the "Windows Class" in the overridden ViewCtrl::CreateParams
. Then, we call the method ViewCtrl::AttachView
in ViewCtrlWindowProc
by using the WM_CREATE
message. When the CView
object is attached to the "ViewCtrl
" window, we call CView::OnInitialUpdate
; and the view content is rendered in the WinForms form, instead of the MFC CFrame
. - We have to synchronize our
ViewCtrl
frame (WinForms form) with the MFC CFrame
for supporting the MDI logic of the legacy MFC application. We make this with the following methods:
OnGotFocus
ProcessDialogKey
SetText
GetParent
- Overriding the
OnHandleDestroed
method detaches the view from the control to the MFC CFrame
and closes the view using dispose. - For autolayout support, the methods
AutoSize
, GetPreferredSize
, and ScaleControl
of the base control (VS2005) or of the special base class LayoutControl
(VS2003) are overridden. The hosted view, connected through the CLaoutView
interface, implements the necessary functionality for these methods. - The
MfcAppAdapter
uses the IWndManager
interface for attaching the created ViewCtrl
controls to the external Framework and for activating the main window when the control receives the GotFocus
event. Otherwise, the external Framework gets the OnClosing
cancel event handler of the MfcAppAdapter
. The Framework can use this handler to support the "CanClose
" function of the legacy application. In the WinForms Framework, we simply connect the OnClosing
cancel event handler with the Closing
event of the MainForm.
UmlEditorCommand
The UmlEditorCommand
enumeration contains all the command IDs which can be handled in the legacy MFC application.
The WPF class WindowsFormsHost
allows us to realize the MfcAppAdapter
as a WPF FrameworkElement
. Moreover, the VS2005 Visual Designer does not support WPF, and it makes the integration of the MfcAdapter
and the WFP Framework easier! The only problem that has to be resolved is that the current version of WindowsFormsHost
does not support the WPF Autolayout manager. In this simple version of WPF WndManager
, we show how it can be realized.
UmlEditorHost
UmlEditorHost
is an extended WindowsFormsHost
with auto-layout support for the hosted control. The standard solution involves overriding the WindowsFormsHost
base methods: MeasureOverride
and ArrangeOverride
.
In the MeasureOverride
, we get the preferred size from the hosted control (method Control.GetPreferredSize
), and decrease it to the desired size, if it's necessary, by keeping the width/height ratio constant.
In the ArrangeOverride
, we scale the hosted control to the preferred size, decreased to the final size, if it's necessary, and multiply it with the zoom factor. Zoom factor is kept in the added dependency property Zoom
, which can be bound with an external layout manager.
WpfPageWndManager
WpfPageWndManager
is the WPF realization of the IWndManager
interface. In the AttachCtrl
method, we simply add the control created by MfcAppAdapter
to UmlEditorHost
. In our case, we don't edit the hosted UML diagrams, so we disable the hosted control and don't use its OnClosing
event handler.
WpfPageWndManager
is responsible for the creation and disposal of MfcAppAdapter
and others created by the MfcAppAdapter
controls. It's is a singleton, and it creates the MfcAppAdapter
instance in its own constructor. In the DisposeContainer
, it saves all the hosts with the created controls. Using the Application.Current.MainWindow.Unloaded
event, we dispose the DisposeContainer
and then the MfcAppAdapter
.
We need MfcHost1Adapter
between WpfPageWndManager
and MfcAppAdapter
if the last one is a .NET 1.x component. The reason for this solution is that .NET 1.x WinForms controls don't have the new layout methods AutoSize
, GetPreferredSize
, and ScaleControl
, and cannot directly support WPF auto-layout. Using a composite control, the .NET 2.0 LayoutControlAdapter
, as the container and the .NET 1.x LayoutControl
as a constituent control, the MfcHost1Adapter
allows us to realize the .NET 1.x WinForms control as a .NET 2.0 WinForms control for the WpfPageWndManager
. The standard methods of the LayoutControlAdapter
: AutoSize
, GetPreferredSize
, and ScaleControl
would be overridden by the custom methods of the contained LayoutControl
. The LayoutControlAdapter
can be used in other projects for hosting .NET 1.x controls in the .NET 2.0 Framework.
Extended MfcAppAdapter
and WndManagerAdapter
allow us to substitute the attached LayoutControl
for the composite LayoutControlAdapter
.
You can create simple demo applications yourself or use the downloaded demos. Try using other managed Frameworks instead of these demos (it's more interesting). Here, I'll describe two demo applications: an MDI forms demo and a WPF page demo.
Create a C# Windows application project and add collections of ToolStripMenuItem
s and ToolStripButton
s (MenuItem
s and ToolBarButton
s for VS2003) to the main form. Add MdiFormsWndManager.dll to your toolbox. Then, drag UmlEditorComponent
from the toolbox to your main form. Now, ToolStripMenuItems
and ToolStripButtons
have become the CommandName
extended properties, where you can set the suitable command for the connected UmlEditorComponent
from the listbox as shown in the picture. Simply compile and run the application:
Create a WinFX Windows application. Add these references - MfcAppAdapter.dll, WpfPageWndManager.dll, and WindowsFormsIntegration.dll. If you use the VS2003 version of the MfcAppAdapter.dll, add MfcHost1Adapter.dll also.
Create a page instead of the window in the picture. We have a simple FlowDocument
with AS:UMLEditorHost
and FrameworkElement
s. The FileSource
property of the AS:UMLEditorHost
defines the path to the UML diagram which has to be added to the document. For the SinglePageViewer
zoom support, the property Zoom
of the AS:UMLEditorHost
is bound with SinglePageViewer.Zoom
in the SinglePageViewer
resource. Simply compile and run the application:
="AS"="AS.MfcHost2"="WpfPageWndManager"
<Page xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
xmlns:AS="AS" Width="800" Height="540">
<SinglePageViewer Name="ZoomSource">
<SinglePageViewer.Resources>
<Style TargetType="{x:Type AS:UMLEditorHost}">
<Setter Property="Zoom"
Value="{Binding ElementName=ZoomSource,
Path=Zoom, Mode=OneWay}"/>
</Style>
</SinglePageViewer.Resources>
<FlowDocument TextAlignment="Left" Background="AliceBlue">
<Paragraph>
<Bold>Hosting MFC application in WPF Page</Bold>
</Paragraph>
<Paragraph KeepTogether="True">
Document - <Italic>UMLEDI1.uml</Italic>
<AS:UMLEditorHost FileSource="../../UMLEDI1.uml"/>
</Paragraph>
<Paragraph KeepTogether="True">
Document - <Italic>UMLEDI2.uml</Italic>
<AS:UMLEditorHost FileSource="../../UMLEDI2.uml"/>
</Paragraph>
</FlowDocument>
</SinglePageViewer>
</Page>
See "Deployment and Installation" from "MfcAdapter - not OLE .Net/WPF container of MFC applications".
- Hosted simple wizards MFC/Dialog application:
I offer the programming guide and source code of a simple wizards MFC/Dialog application DialogWiz
as a starting point for your MFC application hosting. For simplicity, I removed the hosting MFC views support from the MfcAppAdapter
component.
Simply change your MFC application as described in the programming guide. Using MfcAppAdapter.OnCmdMsg()
, you can send commands from a .NET Form to your MFC command handlers and show your dialogs.
Code is compatible with VS 2003-VS 2012, you need only update the project settings.
- MfcAdapter VS2003\VS2005 with hosted
UMLEditor
:
After we unzip, we have two solutions - UMLEditor2003
(VS2003) and UMLEditor2005
(VS2005). The legacy MFC source code (directory Legacy) is common for both solutions. Please don't convert the UMLEditor2003
solution to VS2005 or UMLEditor2005
solution to VS2010 - you need some source code changes in this case.
The UMLEditor2003
solution contains the MfcAppAdapter
, MdiFormsWndManager
, and the MdiFormsDemo
projects. Set the MdiFormsDemo
as the startup project, and then build and run.
The UMLEditor2005
solution has additional WpfPageWndManager
, WpfPageDemo
, and MfcHost1Adapter
projects. WpfPageDemo
and WpfPageWndManager
are compatible with the WinFX Runtime Components 3.0 - Beta 2 (Jan CTP assemblies, version 6.0.5070.0).
Check the reference to WindowsFormsIntegration.dll (normally it's ..\Program Files\Reference Assemblies\Microsoft\WPF\v3.0\WindowsFormsIntegration.dll). Set MdiFormsDemo
or WpfPageDemo
as the startup project, and then build and run.
If you want to use the VS2003 version of the MfcAppAdapter
with WPF, add a reference to the VS2003 MfcAppAdapter.dll in MfcHost1Adapter
. Build MfcHost1Adapter
, and add references to the VS2003 MfcAppAdapter.dll and MfcHost1Adapter.dll in WpfPageWndManager
and WpfPageDemo
, instead of the VS2005 MfcAppAdapter.dll. Rebuild and run.
I tried to keep the encapsulated MFC code as short as possible, so I have not fixed some of the native problems of this code, for example, an unhandled exception when opening a recent file that does not exist.
- MfcAdapter 3.0. Demo VS2019:
MfcAdapter
3.0 does not need a special installation. After extraction from the archive, You will have source code and executable programs of 5 MFC applications:
- "
DialogWiz
" - Simple MFC application without views (only dialogs) created with VS 2005 wizard - "
DialogEditor
" - DIY vector and dialog editor by Johan Rosengren (http://www.codeproject.com/) - "
UMLEditor
" - Revisiting the vector editor by Johan Rosengren (http://www.codeproject.com/) - "
Mesh
" - A small VRML viewer using OpenGL and MFC (http://www.codeproject.com/) - "
SimpleCtrl
" - MFC simple of CLayoutView
, CView
Hosted in WinForms:
- FormsDemo_UmlEditor.exe - Container with hosted MFC applications "
UmlEditor
" and "DialogEditor
" - MdiFormsDemo.exe - Full version of the hosted "
UmlEditor
" - SdiFormsDemo.exe - Full version of the hosted "
DialogEditor
" - FormDemo_SimpleCtrl.exe - Simple test for the
CScrollView,
CListCtrl
- FormsDemo_DialogWiz.exe - hosted "
DialogWiz
"
Hosted in WPF:
- WpfDemo_UmlEditor.exe - Container with hosted MFC applications "
UmlEditor
" and "DialogEditor
" - WpfPageDemo_UmlEditor.exe - WPF
FlowDocument
. Container with hosted MFC applications "UmlEditor
" and "DialogEditor
" - WpfPageDemo_WrlViewer.exe - hosted VRML viewer
Simply run the necessary application.
Source Code
NOTE: MfcAdapter
3.0 demo supports only one configuration: x86 Debug, Unicode, .NET 4.0-4.7
After unzip, we have one VS 2019 solution - \Samples\Src\Samples.sln with all demo projects. All projects build outputs will be copied in the common execute directory \Samples\Src\x86\Debug with necessary MfcAdapter DLLs.
By reference error, simply repeat a build.
I tried to keep the encapsulated MFC code as short as possible, so I have not fixed some of the native problems of this code, for example, compiler warnings, an unhandled exception when opening a recent file that does not exist and others.
Host Your MFC Application
For hosting your MFC application, use - x86 Debug Unicode .NET 4.0-4.7 version of MfcAdapter
:
\Debug:
- MfcAppAdapter.dll, ViewControl.dll, ViewFrameworkElement.dll
\Include:
Help:
- MfcAdapterAPI.chm, ProgrammingGuide.htm
I assume that this article's solution - the MFC extension DLL with the CWinApp
derived class - allows you to host MFC applications in any (not only managed WinForms/WPF) Win32 no MFC Framework by changing the MDI classes and using a suitable wrapper. Otherwise, this may be a useful sample for Visual Studio designer programming in a WinForms component (UmlEditorComponent
) as it supports setting of the Host in the designer generated code.
- 30th December, 2005: Initial post
- 31st May, 2022: Article updated