MfcAdapter – not OLE .NET/WPF Container of MFC Applications

0
27

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 CommandIDs 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)
  1. Create Visual C++ MFC Application “DialogWiz” with default project settings – Multiple document interface, No database support.
  2. 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:

  3. 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)
  4. Change “CLR support” property of MfcCommand.cpp:
  5. 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**************************//

  6. Add to solution new Visual C# Windows Application ” FormsDemo_DialogWiz “. Add reference to DialogWiz.dll, and to MfcAppAdapter.
  7. Overwrite files Form1.cs, Form1.Designer.cs, Form1.resx from the resulted source code to our project.
  8. 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:

  • HostApp.h, LayoutView.h 

Help:

  • MfcAdapterAPI.chm, ProgrammingGuide.htm 

LEAVE A REPLY