MfcAdapter - 非 OLE .NET/WPF MFC 应用程序容器






4.61/5 (12投票s)
如何基于在 WinForms 和 WPF 应用程序中托管 MFC 应用程序,构建非 OLE MFC 应用程序容器。
- 下载 MfcAdapter 2.2 演示版 - 3.4 MB
- (请参阅 "部署和安装" 以仔细安装)
目录
引言
在我第一篇文章 "在 WinForms 和 WPF 应用程序中托管 MFC MDI 应用程序" 的电子邮件中,我收到一个问题 - "使用描述的技术是否可以构建 MFC 应用程序的容器?" 这个任务导致了架构的一些小改动,以使组件更加灵活。这样的工作总是令人愉快的,这也是本文的主要动机。另一方面,结果看起来像一个 OLE 容器,但我没有创建 COM 对象就完成了!
我使用两个应用程序作为示例:Johan Rosengren 的 UmlEditor (MDI) 和 DialogEditor (SDI)。我们可以使用 MfcAdapter
将这两个应用程序嵌入 WinForms 或 WPF 框架中。为简单起见,我使用了基于 RichTextBox
的演示编辑器 "FormsDemo
" (WinFroms) 和 "WPFDemo
" (WPF) 作为这种容器。
容器的架构
主要的变化是将 MfcAdapter
和 MFC 应用程序分成单独的组件。在这种情况下,我们将拥有一个完全独立于实际 MFC 应用程序的 MfcAdapter
。
在所述架构中,低级 .NET 组件是一个混合(托管/非托管)DLL,其中包含一个 MFC 应用程序和托管类 ModuleState
。
ModuleState
是 MFC 应用程序的非托管 AFX_MODULE_STATE
的托管指针。使用 static
方法 ModuleState.GetModuleState()
,MfcAdapter
或其他 C++/CLI 组件可以从必要的混合 DLL 加载 AFX_MODULE_STATE
。之后,MfcAdapter
可以切换到这个 AFX_MODULE_STATE
并使用被托管 MFC 应用程序的非托管 API。此外,混合 MFC DLL 还提供了 CommandID
的托管等效项,这些项可以在 MFC 应用程序中进行处理。
要在 WinForms/WPF 框架中托管 MFC 应用程序,如我在第一篇文章中所述,我们应该
- 保留一个不可见的
CMainFrame
和一个已创建的CView
。我已经更新了应用程序初始化,并在重写的文档模板中将bMakeVisible
参数设置为 "false
" 以实现此目的。好消息是,我们不需要像以前版本的MfcAdapter
那样重写CDocManager
或更改文档。 - 保留被托管对话框的模态属性。将 MFC MainFrame 窗口创建为 Framework MainForm 窗口的子窗口。在 MFC MainFrame 中使用
CNotifyHook
来支持外部框架中的模态 MFC 对话框。 - 自动布局支持。我们必须在托管视图中实现
CLayoutView
类的一些方法:SetPrefferedSize()
、GetPreferredSize()
和Scale()
。
创建混合 DLL 所需的 MFC 代码更改和项目设置的详细描述在上一篇文章中。
中级 组件 MfcAppAdapter
、ViewCtrl
和 ViewFrameworkElemen
是非托管 MFC 应用程序的包装器。这些组件组合成 MfcAdapter
。
MfcAppAdapter
负责封装的 MFC 应用程序的初始化/终止、应用程序的命令接口和文档打开。可以在 ViewCtrl
(WinForms) 或 ViewFrameworkElement
(WPF) 中托管的 MFC 视图可以通过方法 MfcAppAdapter.GetCreatedView()
找到。中级组件不依赖于实际的 MFC 应用程序或实际的 .NET/WPF Framework。示例版本支持 SDI/MDI MFC 应用程序,但也可以添加对其他 MFC 应用程序类型的支持:基于对话框和多顶层文档。
高级 组件是 FormsDemo
(WinForms) 和 WPFDemo
(WPF)。这两个编辑器都包含两个托管的 MFC 应用程序及其文档:UmlEditor.dll + UmlEdi1.uml 和 DialogEditor.dll + DlgEdi1.dlg。两个编辑器都支持
- 简单布局托管的
Controls
(FormsDemo
) 或托管的FrameworkElements
(WPFDemo
) - 托管 MFC 应用程序中的
ToolStrip
(FormsDemo
) 或ToolBar
(WPFDemo
) 的UpdateUI
和事件处理。
布局
虽然 MFC 应用程序的初始化和 MFC 视图的托管相对容易解决,但布局是 MFC 与 WinForms/WPF 集成中的主要问题之一。为了支持托管视图中的必要布局,CView
接口是不够的,因此我使用了 CLayoutView
接口。通过这个接口,我在描述的示例中实现了相对简单的布局。如果您需要其他布局实现,可以重写方法:ViewCtrl.GetPrefferedSize()
、ViewCtrl.ScaleControl()
并使用其他接口。
布局问题也是 WindowsFormsHost
类的问题。该类试图为托管的 WinForms Control
实现 WPF 布局。不幸的是,即使在简单的情况下,我也无法利用 WindowsFormsHost
。这是 WinForms 和 WPF 布局纯粹兼容性的结果,还是仅仅是编程错误,我不知道。例如
<!--Test WindowsFormsHost-->
<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>
第一个测试用例会引入一个循环,第二个会引入一个异常。
因此,我使用了 ViewFrameworkElemen
类来代替 ViewCtrl
和 WindowsFormsHost
类来在 WPF 中托管视图。在这种情况下,我们应该实现必要的 WPF 布局,而不是实现 WinForms 布局并用 WindowsFormsHost
"转换" 到 WPF。与 ViewCtrl
一样,我们可以重写方法:ViewFrameworkElement.OnMeseareOverride()
和 ViewFrameworkElement.ArrangeOverride()
来实现任何其他布局,例如支持分辨率无关的布局。
快速入门:托管简单的 MFC/对话框应用程序
为了简单起见,我在此描述了在 .NET Framework 中托管简单的 MFC/对话框应用程序 - 未托管视图和 VS 设计器支持。此示例的目的是帮助您开始托管 MFC 应用程序。它包含 8 个步骤,您需要创建 Visual Studio 2005 向导 MFC/对话框应用程序 "DialogWiz" 并在 "FormsDemo_DialogWiz" WinForms .Net 应用程序中托管它。最终的源代码可以在 MfcAdapter 2.2 示例中找到。
注意: 由于 MfcAdapter 2.2 不支持 CWinAppEx
,因此您只能在 VS2005 中创建此示例!如果您使用 VS2010,请使用 MfcAdapter 2.2 示例中的 "DialogWiz" 示例。
使用公共语言运行时支持进行编译 | 公共语言运行时支持 (/clr) |
- 创建 Visual C++ MFC 应用程序 "DialogWiz",使用默认项目设置 - 多文档界面,无数据库支持。
- 将
NotifyHook.cpp
、NotifyHook.h
、CHostApp.h
、MfcCommand.cpp
源文件从最终源代码复制到您的解决方案目录,并将其添加到 "DialogWiz" 项目。现在我们有了这个项目树。 - 更改项目设置
配置类型 动态库 (.dll) 其他包含目录 "$(ProjectDir)" 调试信息格式 程序数据库 (/Zi) 启用 C++ 异常 带 SHE 异常 (/EHa) 启用最小重新生成 否 基本运行时检查 默认值 创建/使用预编译头文件 不使用预编译头文件 输出文件 $(OutDir)\$(ProjectName).dll 可调试程序集 是 (/ASSEMBLYDEBUG) - 更改 MfcCommand.cpp 的 "CLR 支持" 属性
- 按最终源代码中的方式更改 DialogWiz.cpp、DialogWiz.h 和 MainForm.cpp、MainForm.h。我用注释跟踪了所有更改。
<code>//******AS update start**************************//
<code>//******AS update end**************************//
- 向解决方案添加新的 Visual C# Windows 应用程序 "FormsDemo_DialogWiz"。添加对 DialogWiz.dll 和 MfcAppAdapter 的引用。
- 用最终源代码中的
Form1.cs
、Form1.Designer.cs
、Form1.resx
文件覆盖我们的项目。 - 设置调试配置并将 "FormsDemo_DialogWiz" 项目设置为启动项目。按 F5 - 祝你好运!
构建先决条件
由于 MfcAdapter 使用 MFC 库,您必须避免混合不同版本的 MfcAdapter 程序集(Debug 和 Release,不同的字符集,不同的 MFC 或 CLR 版本)。您可以通过项目设置中的生成后事件简单地复制必要的 Debug 或 Release 程序集。
兼容性
MfcAdapter 控件 | 类型 | 支持 MFC 应用程序容器 | 支持的 MFC 视图 |
ViewCtrl | WinForms | 是 | CWnd, CView, CScrollView |
ViewFrameworkElement | WPF | 是 | CWnd, CView, CScrollView |
ViewFrameworkElementEx | WPF | 否 | CWnd, CView, CScrollView, CCtrlView, CListView, CTreeView, CEditView, CRichEditView |
所有 MfcAdapter 类都支持托管 CWnd
、CView
或基于 CScrollView
的客户视图。您可以直接嵌入控件对象(如 CListCtrl
、CHeaderCtrl
等)或例如 COleDropTarget
作为类成员。
无论如何,请不要在 CView::OnCreate
中创建和初始化继承自 CWnd
的视图类成员。此代码应从 OnCreate
方法移至 OnInitialUpdate
。这样,这些成员将在视图附加到 WinForms Control
或 WPF 中的 HwndHost
元素之后创建。
ViewFrameworkElementEx
扩展了 ViewFrameworkElement
以支持托管基于 CCtrlView
、CListView
、CTreeView
、CEditView
、CRichEditView
的客户视图。ViewFrameworkElementEx
就像 ViewFrameworkElement
一样,可以托管基于 CWnd
、CView
或 CScrollView
的客户视图。但它只能用于托管单个 MFC 应用程序。请参阅 MfcAdapter 示例中的 "RowList" 示例。
MfcAdapter 的当前版本不支持托管基于 CFormView
的视图和基于 CWinAppEx
的应用程序。
MfcAdapter 源代码兼容 x86、x64 Windows、MFC 版本 7.1 (VS2003) 任何配置(ASCII\Unicode\Mulibyte)以及 .Net 2.0 至 4.5。
部署和安装
注意: 要使用 MfcAdapter 2.2 演示版,需要安装 VS 2010。
MfcAdapter 2.2 不需要特殊安装。从存档解压后,您将获得 5 个 MFC 应用程序的源代码和可执行程序。
- "DialogWiz" - 使用 VS 2005 向导创建的简单 MFC 应用程序(仅对话框,无视图)。
- "DialogEditor" - Johan Rosengren 的 DIY 矢量和对话框编辑器 (https://codeproject.org.cn/)
- "UMLEditor" - Johan Rosengren 的矢量编辑器重制版 (https://codeproject.org.cn/)
- "Mesh" - 一个使用 OpenGL 和 MFC 的小型 VRML 查看器 (https://codeproject.org.cn/)
- "RowList" - CListView、CLayoutView、CView 的 MFC 示例。
托管在 **WinForms** 中
- FormsDemo_UmlEditor.exe - 包含托管 MFC 应用程序 "UmlEditor" 和 "DialogEditor" 的容器
- MdiFormsDemo.exe - 托管 "UmlEditor" 的完整版本
- SdiFormsDemo.exe - 托管 "DialogEditor" 的完整版本
- FormDemo_SimpleCtrl.exe -
CScrollView
、CListCtrl
的简单测试 - FormsDemo_DialogWiz.exe - 托管 "DialogWiz"
托管在 **WPF** 中
- WpfDemo_UmlEditor.exe - 包含托管 MFC 应用程序 "UmlEditor" 和 "DialogEditor" 的容器
- WpfPageDemo_UmlEditor.exe - WPF
FlowDocument
。包含托管 MFC 应用程序 "UmlEditor" 和 "DialogEditor" 的容器 - WpfPageDemo_RowList.exe - 托管 "RowList"
- WpfPageDemo_WrlViewer.exe - 托管 VRML 查看器
只需运行相应的应用程序。
源代码
注意: MfcAdapter 2.2 演示版仅支持一种配置:x86 Debug、Unicode、.Net 4.0。
解压后,我们将获得一个 VS 2010 解决方案 - \Samples\Src\Samples.sln,其中包含所有演示项目。所有项目的构建输出将复制到公共可执行目录 \Samples\Src\Debug,并包含必要的 MfcAdapter dll。
如果出现引用错误,请简单地重新构建。
我尽量使封装的 MFC 代码尽可能简洁,因此我没有修复此代码中的一些原生问题,例如编译器警告、打开不存在的最近文件时未处理的异常等。
托管您的 MFC 应用程序
要托管您的 MFC 应用程序,请使用 - x86 Debug Unicode .Net 4.0 版本的 MfcAdapter
\Debug
MfcAppAdapter.dll, ViewControl.dll, ViewFrameworkElement.dll
\Include
HostApp.h, LayoutView.h
帮助
MfcAdapterAPI.chm, ProgrammingGuide.htm