Contents
In an e-mail to my first article "Hosting a MFC MDI application from within WinForms and WPF applications" I received a question - "Is it possible to build a container of MFC applications using the described technology?" This task has caused a little change of architecture to make components more flexible. Such work always brings pleasure, and it was the main motivation for this article. On the other hand, the result looks like an OLE container, but I have done this without creating COM objects!
I used two applications as samples: UmlEditor (MDI) and DialogEditor (SDI) by Johan Rosengren. We can simply embed both these applications in WinForms or the WPF framework, using MfcAdapter
. For simplicity, I used RichTextBox
based demo editors "FormsDemo
" (WinFroms) and "WPFDemo
" (WPF) as such kind of a container:
The main change is to divide MfcAdapter
and MFC application in separate components. In this case, we will have an MfcAdapter
that is fully independent from an actual MFC application:
The low level .NET component in the described architecture is a Mixed (managed/unmanaged) DLL with an MFC application and the managed class ModuleState
inside.
The ModuleState
is a managed pointer to unmanaged AFX_MODULE_STATE
of the MFC application. With static
method ModuleState.GetModuleState()
the MfcAdapter
or other C++/CLI component can load AFX_MODULE_STATE
from necessary Mixed DLL. After this MfcAdapter
can switch to this AFX_MODULE_STATE
and use the unmanaged API of the hosted MFC application. Additionally, the Mixed MFC DLL provides managed equivalents to CommandID
s which can be processed in the MFC application.
To host MFC application in WinForms/WPF framework, as described in my first article, we should:
- Keep an invisible
CMainFrame
and a created CView
. I have updated the application initialization and set bMakeVisible
parameter to "false
" in the overridden document template to implement this. The good news is we don't need to override CDocManager
and to change document like in previous version of MfcAdapter
. - Keep the modal properties of the hosted dialogs. Create the MFC MainFrame window as child of Framework MainForm window. Use the
CNotifyHook
in the MFC MainFrame for supporting modal MFC dialogs in external framework. - Auto-layout support. We have to implement some methods from
CLayoutView
class in our hosted view: SetPrefferedSize()
, GetPreferredSize()
, and Scale()
.
The detailed description of necessary MFC code changes and Project settings necessary for creating mixed DLL are described in the previous article.
The mid-level component's MfcAppAdapter
, ViewCtrl
and ViewFrameworkElemen
are wrappers to the unmanaged MFC application. These components combine into MfcAdapter
.
MfcAppAdapter
is responsible for the initialization/termination of the encapsulated MFC application, the command interface to the application and the document opening. The MFC View created in hosted MFC application can be found with method MfcAppAdapter.GetCreatedView()
and can be hosted in ViewCtrl
(WinForms) or ViewFrameworkElement
(WPF). Mid-level components are not dependant on the actual MFC application or on the actual .NET/WPF Framework. The sample version supports SDI/MDI MFC applications, but it is possible to add support for other MFC application types: Dialog based and Multiple top-level documents.
The high level components are FormsDemo
(WinForms) and WPFDemo
(WPF). Both editors have two hosted MFC applications with documents: UmlEditor.dll + UmlEdi1.uml and DialogEditor.dll + DlgEdi1.dlg. Both editors support:
- Simply layout of hosted
Controls
(FormsDemo
) or hosted FrameworkElements
(WPFDemo
) UpdateUI
and event handling of the ToolStrip
(FormsDemo
) or ToolBar
(WPFDemo
) in hosted MFC applications.
While the initialization of MFC application and the hosting of MFC View is solved relatively easily, Layout is one of the main problems of the integration MFC and WinForms/WPF. To support necessary Layout in a hosted View, the CView
interface is not enough, therefore I used the CLayoutView
interface. With this interface, I realized a relatively simple Layout in a described example. If you need another layout realization, you can override methods: ViewCtrl. GetPrefferedSize()
, ViewCtrl.ScaleControl()
and use another interface.
Layout problems are also problems with WindowsFormsHost
class. This class tries to realize the WPF Layout for hosted WinForms Control
. Unfortunately, I could not take advantage of WindowsFormsHost
even in simple cases. Is this the result of pure compatibility of WinForms and WPF Layout or simply programming bugs, I don't know. For example:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms">
<FlowDocumentPageViewer>
<FlowDocument TextAlignment="Left" Background="AliceBlue" >
<Paragraph>
<Bold>Test WindowsFormsHost</Bold>
</Paragraph>
<Paragraph>
TestCase1: change Zoom.
</Paragraph>
<Paragraph>
TestCase2: remove Background="Yellow" attribute from wrapped button.
</Paragraph>
<Paragraph>
<Button>WPF button</Button>
</Paragraph>
<Paragraph>
<WindowsFormsHost Background="Yellow">
<wf:Button Text="Windows Forms button" FlatStyle="Flat"/>
</WindowsFormsHost>
</Paragraph>
</FlowDocument>
</FlowDocumentPageViewer>
</Page>
The first Test Case would introduce a cycle, the second - exception.
Thus, instead of ViewCtrl
and WindowsFormsHost
classes, I used ViewFrameworkElemen
class for hosting View in WPF. In this case, we should implement the necessary WPF Layout instead of implementation of WinForms Layout and its "conversion" to WPF with WindowsFormsHost
. As well as in case of ViewCtrl
we can override methods: ViewFrameworkElement. OnMeseareOverride()
and ViewFrameworkElement.ArrangeOverride()
to realize any other Layout, for example, to support the resolution-independent layout.
For simplicity, I describe here hosting of simple MFC/Dialogs application in .NET Framework - without hosting of views and VS designer support. The purpose of this sample is to help you to start with your MFC application hosting. It consists of 8 steps which you need to create Visual Studio 2005 wizards MFC/Dialogs application "DialogWiz" and to host it in “FormsDemo_DialogWiz” WinForms .Net application. The resulted source code you can find in MfcAdapter 2.2 samples.
NOTE: Because MfcAdapter 2.2 does not supports CWinAppEx
, you can create this sample only in VS2005! If you use VS2010, please use the "DialogWiz" sample from MfcAdapter 2.2 samples.
Compile with Common Language Runtime Support | Common Language Runtime Support (/clr) |
- Create Visual C++ MFC Application "DialogWiz" with default project settings - Multiple document interface, No database support.
- Copy source code files
NotifyHook.cpp
, NotifyHook.h
, CHostApp.h
, MfcCommand.cpp
from resulted source code to you solution directory and add it to your "DialogWiz" project. Now we have this project tree:
- Change Project setting:
Configuration Type | Dynamic Library (.dll) |
Additional Include Directories | "$(ProjectDir)" |
Debug Information Format | Program Database (/Zi) |
Enable C++ Exceptions | With SHE Exceptions (/EHa) |
Enable Minimal Rebuild | No |
Basic Runtime Checks | Default |
Create/Use Precompiled Headers | Not Using Precompiled Headers |
Output File | $(OutDir)\$(ProjectName).dll |
Debuggable Assembly | Yes (/ASSEMBLYDEBUG) |
- Change "CLR support" property of MfcCommand.cpp:
- Change DialogWiz.cpp DialogWiz.h and MainForm.cpp MainForm.h as in resulted source code. I track all changes with comments:
<code>//******AS update start**************************//
<code>//******AS update end**************************//
- Add to solution new Visual C# Windows Application " FormsDemo_DialogWiz ". Add reference to DialogWiz.dll, and to MfcAppAdapter.
- Overwrite files
Form1.cs
, Form1.Designer.cs
, Form1.resx
from the resulted source code to our project. - Set Debug Configuration and " FormsDemo_DialogWiz " project as StartUp Project. Press F5 - Good luck!
Because the MfcAdapter uses MFC library, you have to avoid mix different version of MfcAdapter assemblies (Debug and Release, different character set, different MFC or CLR versions). You can simply copy necessary Debug or Release assemblies by Post Build Event in project settings.
MfcAdapter Control | Type | Support of MFC applications container | Supported MFC Views |
ViewCtrl | WinForms | Yes | CWnd, CView, CScrollView |
ViewFrameworkElement | WPF | Yes | CWnd, CView, CScrollView |
ViewFrameworkElementEx | WPF | No | CWnd, CView, CScrollView, CCtrlView, CListView, CTreeView, CEditView, CRichEditView |
All MfcAdapter classes suport the hosting of CWnd
, CView
or CScrollView
based customers views. You can directly embed a control object (like CListCtrl
, CHeaderCtrl
and so on+) or for example COleDropTarget
as class member in your class.
In any case don't use CView::OnCreate
for creating and initializing of view class members inherited from CWnd
. This code should be removed from the OnCreate
method to the OnInitialUpdate
. In this case, these members will be created after the view attached to the WinForms Control
or to HwndHost
element in WPF.
ViewFrameworkElementEx
extends ViewFrameworkElement
to support the hosting of CCtrlView
, CListView
, CTreeView
, CEditView
, CRichEditView
based customer’s views. ViewFrameworkElementEx
can host just as ViewFrameworkElement
CWnd
, CView
or CScrollView
based customer’s views. But it can be used only for hosting single MFC application. See the sample “RowList“ from MfcAdapter samples.
The current version of the MfcAdapter does not support the hosting of CFormView
based views and CWinAppEx
based applications.
MfcAdapter source code compatible with x86, x64 Windows, MFC version from 7.1 (VS2003) any configuration (ASCII\Unicode\Mulibyte) and .Net from 2.0 to 4.5.
NOTE: For using MfcAdapter 2.2 Demo you need installed VS 2010.
MfcAdapter 2.2 does not need a special installation. After extraction from the archiv, 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/)
- "RowList" - MFC sample of CListView, 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_RowList.exe - hosted "RowList"
- WpfPageDemo_WrlViewer.exe - hosted VRML viewer
Simply run the necessary application.
Source Code
NOTE: MfcAdapter 2.2 Demo support only one configuration: x86 Debug, Unicode, .Net 4.0.
After unzip, we have one VS 2010 solution - \Samples\Src\Samples.sln with all demo projects. All projects build outputs will be copied in the common execute directory \Samples\Src\Debug with necessary MfcAdapter dll's.
By reference error, simple 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 host your MFC application use - x86 Debug Unicode .Net 4.0 version of MfcAdapter:
\Debug:
MfcAppAdapter.dll, ViewControl.dll, ViewFrameworkElement.dll
\Include:
Help:
MfcAdapterAPI.chm, ProgrammingGuide.htm