您可能仍然在维护用微软基础类库(Microsoft Foundation Classes(MFC))构建的旧的 Windows 应用程序,而现在却有客户要求 Linux 版本,该怎么办呢?在您的团队中可能有技术熟练的 MFC 开发人员,但如何达到加速 Linux 开发呢?别急;本文就是针对您这种情况而写的。依靠 wxWindows(一种用于 C++ 和 Python 的可移植 GUI 工具箱)的帮助,我将以多文档界面(Multiple Document Interface (MDI))文本编辑器为例向您演示如何将仅 Windows 的 MFC 应用程序移植到 Linux。类似这样的小型应用程序有助于我们将讨论集中在移植框架的具体细节上,从而避免我们迷失在代码的汪洋中。可以在本文后面的 参考资料一节中获取完整的 MFC 应用程序和 wxWindows 应用程序的源代码。
文档/视图概述
我将演示的应用程序使用众所周知的文档/视图体系结构,因为它可以象大多数应用程序一样处理文档。即使您的应用程序不使用文档/视图体系结构,我也建议您读下去。只要您已在转向这种框架,您就可能想要添加这项功能。
在我的关于 wxWindows 的 前一篇文章中,曾经指出过 MFC 和 wxWindows 之间具有某些相似性。字符串类 CString
、 wxString
和事件系统之间都非常相似。但还不止这些相似性。wxWindows 工具箱还提供对文档/视图体系结构的类 MFC 支持。
我将从核心类的比较开始。下表列出了两种框架的文档/视图体系结构所涉及的类。
表 1. 文档/视图类比较
类 |
MFC 类 |
wxWindows 类 |
文档(Document) |
CDocument |
wxDocument |
视图(View) |
CView |
wxView |
编辑视图(Edit view) |
CEditView |
n/a |
模板类(Template class) |
CMultiDocTemplate |
wxDocTemplate |
MDI 父框架(MDI parent frame) |
CMDIFrameWnd |
wxDocMDIParentFrame |
MDI 子框架(MDI child frame) |
CMDIChildWnd |
wxDocMDIChildFrame |
文档管理器(Document manager) |
n/a |
wxDocManager |
除编辑视图类以外,每个 MFC 类都有其对应的 wxWindows 类。(最后一项中 MFC 的部分为空,因为 MFC 没有独立的文档管理器类。由应用程序类 CWinApp
内部处理文档。)下列 UML 图演示了这些类之间的关系:
图 1. MFC 类 图 2. wxWindows 类
应用程序
每个框架都提供一个表示应用程序本身的类。MFC 应用程序类声明了一个构造器、一个用于初始化的方法、一个用于事件处理的方法和一个消息映射表。您需要这个消息映射表声明和事件处理方法,因为应用程序的“about”对话框将由该类处理。
应用程序类:MFC
class CPortMeApp : public CWinApp
{
public:
CPortMeApp();
virtual BOOL InitInstance();
afx_msg void OnAppAbout();
DECLARE_MESSAGE_MAP()
};
|
注:最初创建 MFC 应用程序时,我使用 Microsoft Visual Studio 所包含的应用程序向导来创建,但在我的代码片段中,我将不给出由向导生成的、有时会使人迷惑的注释( //{{AFX_MSG
及类似的东西)。完整的源代码,请参阅 ZIP 压缩文档。
对应的 wxWindows 类看起来略微有些不同。它也声明了一个构造器及一个用于初始化的方法,但却不需要任何东西来处理消息。如同您随后将看到的一样,在主框架类中处理“about”对话框。
应用程序类:wxWindows
class PortedApp : public wxApp
{
public:
PortedApp();
bool OnInit();
int OnExit();
protected:
wxDocManager* m_docManager;
};
|
正如下面所描述的那样,这个类需要一个 wxDocManager
属性来处理在初始化方法 OnInit()
中创建的模板。应用程序退出时,清理方法 OnExit()
将删除这个 wxDocManager
对象。
所有应用程序都需要其入口点(也称为 main()
或 WinMain()
)。在实现这一点的方法上,两种框架略微有些不同。在 MFC 中,象这样创建应用程序类的静态对象:
在 wxWindows 中,则象这样使用 IMPLEMENT_APP()
宏:
如果对该宏所做的事情感兴趣,请查看头文件 wx/app.h
中它的定义,在可下载的源代码中找到该头文件。基本上,它是为所使用的平台插入适当的入口点函数。创建了应用程序类的对象之后,需要对其进行初始化。对于 MFC,Microsoft 建议不使用应用程序对象的构造器来初始化对象。而是应该使用其 InitInstance()
方法。要执行任何清理,请实现 ExitInstance()
方法。
虽然应用程序初始化有很多事情要做,这里我将只着重讨论与文档/视图有关的代码。要建立文档/视图框架, InitInstance()
方法必须创建一个 CMultiDocTemplate
,如下所示:
创建文档/视图代码:MFC
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_PORTMETYPE,
RUNTIME_CLASS(CPortMeDoc),
RUNTIME_CLASS(CChildFrame),
RUNTIME_CLASS(CPortMeView));
|
wxWindows 应用程序提供 OnInit()
方法来做任何初始化工作,并提供 OnExit()
用于清理。要在 wxWindows 应用程序中建立文档/视图框架, OnInit()
方法必须象这样创建 wxDocTemplate
:
创建文档/视图代码:wxWindows
m_docManager = new wxDocManager();
wxDocTemplate* pDocTemplate;
pDocTemplate = new wxDocTemplate(
m_docManager, "Pom", "*.pom", "", "pom", "Pom Doc", "Text View",
CLASSINFO(PortedDoc),
CLASSINFO(PortedView));
|
MFC 和 wxWindows 所做的事情基本上相同。框架需要关于哪个文档同哪个视图有关以及这个组合处理哪种文档的信息。类型包括文档的描述性名称以及这类文档的文件扩展名。两种框架都使用模板来处理这一问题(请注意这与标准 C++ 模板没有什么关系)。
MFC CMultiDocTemplate
也保存关于同文档相关联的子框架的信息,并且该模板被添加到管理该模板的应用程序对象中。wxWindows wxDocTemplate
额外需要一个管理模板的 wxDocManager
对象。请记住, wxDocManager
是 wxWindows 应用程序的应用程序类的一个属性。
完成文档/视图框架初始化之后,就创建了应用程序的主框架。在 MFC 应用程序的 InitInstance()
方法中,按如下创建一个主框架:
创建主框架:MFC
CMainFrame* pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
return FALSE;
m_pMainWnd = pMainFrame;
pMainFrame->ShowWindow(m_nCmdShow);
pMainFrame->UpdateWindow();
|
调用 pMainFrame->LoadFrame(IDR_MAINFRAME)
从资源文件装入所有关于主框架的信息。
在 wxWindows 中,可以从资源文件建立菜单、对话框以及控件,或者可以在代码中创建它们。我更喜欢在代码中创建它们,但如果您愿意将代码同资源分离,就应该看一看 wxWindows 资源系统(请参阅 wxWindows 文档中的主题概述)。
创建主框架:wxWindows
m_mainFrame = new MainFrame(m_docManager, (wxFrame*) NULL, "DocView Demo",
wxPoint(0, 0), wxSize(500, 400),
wxDEFAULT_FRAME_STYLE);
// Set up menu bar...
m_mainFrame->SetMenuBar(menu_bar);
m_mainFrame->Centre(wxBOTH);
m_mainFrame->Show(TRUE);
SetTopWindow(m_mainFrame);
|
在查看应用程序初始化完成后发生了什么事情之前,让我向您演示每个框架的文档和视图类。
文档
文档保存应用程序处理的基于文件的数据。它负责从文件装入该数据,并在必要的时候将该数据保存回文件。该 MFC 类的声明类似于这样:
文档类声明:MFC
class CPortMeDoc : public CDocument
{
protected:
CPortMeDoc();
DECLARE_DYNCREATE(CPortMeDoc)
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
virtual ~CPortMeDoc();
DECLARE_MESSAGE_MAP()
};
|
请注意:该类有一个受保护(protected)的构造器。决不要直接创建该类的任何对象;相反,框架使用序列化来创建文档。因此,需要使用 DECLARE_DYNCREATE()
宏。wxWindows 类的声明类似于这样:
文档类声明:wxWindows
class PortedDoc : public wxDocument
{
public:
virtual bool OnSaveDocument(const wxString& filename);
virtual bool OnOpenDocument(const wxString& filename);
virtual bool IsModified() const;
virtual void Modify(bool mod);
private:
DECLARE_DYNAMIC_CLASS(PortedDoc)
};
|
DECLARE_DYNAMIC_CLASS()
宏是必需的,因为 wxWindows 就象 MFC 一样动态地创建该类的对象。
视图
如果 MFC 应用程序处理文本文档,那么从 CEditView
派生视图类是一个好主意。该类已经在其客户机窗口内提供了基本的编辑功能和文本控件。这里我遵循自己的建议,MFC 视图类的声明类似于这样:
视图类声明:MFC
class CPortMeView : public CEditView
{
protected:
CPortMeView();
DECLARE_DYNCREATE(CPortMeView)
public:
CPortMeDoc* GetDocument();
virtual void OnDraw(CDC* pDC);
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
virtual ~CPortMeView();
DECLARE_MESSAGE_MAP()
};
|
在 wxWindows 中,(还)没有专门的编辑视图。但是创建您自己的编辑视图却很容易。只需在视图框架内有一个由 wxTextCtrl
派生的文本控件。视图类将控件和框架都当作类属性来处理。因此 wxWindows 声明类似于这样:
视图类声明:wxWindows
class PortedView : public wxView
{
public:
MyTextCtrl* textsw;
PortedView();
bool OnCreate(wxDocument* doc, long flags);
void OnDraw(wxDC* dc);
bool OnClose(bool deleteWindow = TRUE);
private:
wxMDIChildFrame* CreateChildFrame(wxDocument* doc, wxView* view);
wxFrame* frame;
DECLARE_DYNAMIC_CLASS(PortedView)
};
|
除了常见的用于窗口创建、窗口重画以及窗口关闭的事件处理程序之外,该类还有一个创建框架并填充其菜单栏的方法。
虽然拥有公共(public)属性不是一个好主意,但是为简单起见,我在这里还是这么做。在您的应用程序中应该避免这么做(我将在以后的版本中除去它)。
主框架
既然已经有了处理和显示数据的文档和视图类,并且也有了处理文档/视图框架的应用程序类,现在需要一个主框架类来同用户交互。同样,两种框架提供类似的功能,而实际实现有略微不同。MFC 主框架类将一个状态栏和一个工具栏作为属性保存,同时 MFC 主框架类还提供一些方法来处理窗口创建,以及声明消息映射表。请注意:该类是从 CMDIFrameWnd
派生而来的,因为该应用程序有一个 MDI 界面。
主框架类:MFC
class CMainFrame : public CMDIFrameWnd
{
public:
CMainFrame();
virtual ~CMainFrame();
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
protected:
CStatusBar m_wndStatusBar;
CToolBar m_wndToolBar;
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
DECLARE_MESSAGE_MAP()
private:
DECLARE_DYNAMIC(CMainFrame)
};
|
wxWindows 主框架类将菜单作为属性保存,还具有创建其工具栏的方法,以及声明一个事件表外加一个处理应用程序的 about 对话框的方法。因为这个应用程序有一个 MDI 界面,所以该类是从 wxDocMDIParentFrame
派生而来。
主框架类:wxWindows
class MainFrame : public wxDocMDIParentFrame
{
public:
wxMenu* editMenu;
MainFrame(wxDocManager* manager, wxFrame* frame, const wxString& title,
const wxPoint& pos, const wxSize& size, long type);
void OnAbout(wxCommandEvent& event);
void RecreateToolbar();
private:
DECLARE_CLASS(MainFrame);
DECLARE_EVENT_TABLE();
};
|
全部工作原理
移植完成了。我来回顾一下实现这一点所需做的事情。MFC 应用程序类(派生自 CWinApp
)移植到派生自 wxApp
的应用程序类,包括初始化代码。将 MFC 文档类(派生自 CDocument
)移植到派生自 wxDocument
的文档类。将 MFC 视图类(派生自 CView
)移植到派生自 wxView
的文档类。除了这些同文档/视图有关的类之外,将 MFC 主框架类(派生自 CMDIFrameWnd
)移植到派生自 wxDocMDIParentFrame
的类。
在其目前的版本中,该应用程序已经能够做很多工作。可以处理多个文本文件。可以打开、编辑并保存这些文件,并且应用程序保持最近打开文件的历史记录。如果不得不从头编写这些功能,那么将需要更多行代码 ― 更多行代码意味着要测试和维护更多行。在两种框架中,都使用了专门的命令标识符来处理常用的同文档/视图有关的命令。常用的文件命令是新建、打开和保存。常用的编辑命令是剪切、复制和粘贴。其它经常使用的命令有打印命令(这个应用程序中还没有实现该命令)和帮助命令。下表列出了这两种框架结构的文档/视图体系结构所包含的命令标识符。
表 2. 标准的命令标识符
MFC |
wxWindows |
ID_FILE_OPEN |
wxID_OPEN |
ID_FILE_CLOSE |
wxID_CLOSE |
ID_FILE_NEW |
wxID_NEW |
ID_FILE_SAVE |
wxID_SAVE |
ID_FILE_SAVE_AS |
wxID_SAVEAS |
ID_EDIT_CUT |
wxID_CUT |
ID_EDIT_COPY |
wxID_COPY |
ID_EDIT_PASTE |
wxID_PASTE |
ID_APP_EXIT |
wxID_EXIT |
ID_EDIT_UNDO |
wxID_UNDO |
ID_EDIT_REDO |
wxID_REDO |
ID_HELP_INDEX |
wxID_HELP |
ID_FILE_PRINT |
wxID_PRINT |
ID_FILE_PRINT_SETUP |
wxID_PRINT_SETUP |
ID_FILE_PRINT_PREVIEW |
wxID_PREVIEW |
窗口管理器
如果您对 Linux 开发真的很感兴趣,则可能已经做了一些研究,发现 Linux 上有不同的窗口管理器。曾经是 MFC 开发人员的您可能觉得这很奇怪。在进行 Windows 开发时,您无须操心不同的窗口管理器,因为只有一个窗口管理器。
开始 wxWindows 开发时,您可能想知道您的应用程序是否将运行在两个主流的窗口管理器 ― K 桌面环境(K Desktop Environment (KDE))和 GNOME ― 之上。我已经在 Microsoft Windows NT 4.0、使用公共桌面环境(Common Desktop Environment (CDE))的 Sun Solaris 2.6 以及使用 KDE 的 Linux 2.2 上使用了 wxWindows 工具箱,没有任何问题。由于 wxWindows 仅仅是一个建立在其它低级别 GUI 工具箱上的高级别层次,所以可以选择如何构建 Linux 应用程序。可以使用 Motif 版本(或者还要更好一些的,免费 Lesstif)或者 wxWindows 的 GTK+ 版本。GTK+ 是 GNOME 使用的基础窗口构件工具箱,但是它在 KDE 下也运行得非常好。我建议您使用 GTK+ 版本,因为它通常更新,有许多开发人员对它进行开发,而且有用户在使用它。因此,如果您希望获得更多帮助,则可以求助于邮件列表或新闻组,询问关于 GTK+ 版本的问题。
下面是前前后后的抓屏,可以让您对移植的应用程序有一个大致的认识。
图 3. 原始的 MFC 应用程序 图 4. Windows 上的 wxWindows 应用程序 图 5. Linux/KDE 上的 wxWindows 应用程序
真实的故事
wxWindows 工具箱不只是书呆子的另一个玩具。它成熟、稳定,并且人们在实际应用程序中用它来解决实际问题。
wxWindows 项目创始人 Julian Smart 目前致力于 Red Hat,他已经将 eCos 配置工具(eCos Configuration Tool)移植到了 wxWindows。eCos 配置工具是一个配置 eCos 嵌入式操作系统的图形工具。人们已将最初的 MFC 应用程序移植到了 wxWindows,尤其是在 Linux 上(参阅 参考资料)。 SciTech Software 的人员也已经使用 wxWindows 来全部重新开发用于 SciTech Display Doctor 产品的前端 GUI。IBM 已经获得了 SciTech Display Doctor 的一个专门版本的许可证,IBM 现在将其作为 IBM 所有基于 OS/2 Warp 的操作系统(包括 Warp Client、Workspace On Demand 及 Warp Server for e-business)的主要显示驱动程序来分发。SciTech Software 对 wxWindows 社区做出了积极的贡献,它提交了扩展包 wxApplet、wxUniversal 和 wxMGL。使用 wxMGL,wxWindows 应用程序甚至可以运行在 DOS 和其它嵌入式操作系统之上,例如 QNX、RT-Target、SMX 等等。SciTech Software 全力资助 wxUniversal 和 wxMGL 项目(参阅 参考资料)。
结束语
本文演示了使用 wxWindows 工具箱将 MFC 文档/视图框架的 Windows 应用程序移植到 Linux 的基本原理。wxWindows 工具箱同 MFC 框架有一些相似性,从而帮助 MFC 开发人员可以达到加速 Linux 开发。有了这些基础知识,您应该能够将最棒的应用程序移植到 Linux。但不要忘了:wxWindows 不是只能用于 Linux。也许该是考虑在其它 UNIX 或者甚至 Macintosh 上来运行您的应用程序一个版本的时候了。
参考资料
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文.
- 从 Markus 的主页下载本文所演示的原始应用程序和移植的应用程序的 完整源代码。
- 有关 wxWindows 的概述,请阅读 Markus 的文章 细述 wxWindows( developerWorks,2001 年 2 月)。
-
wxWindows 主页是 wxWindows 社区成员要去的主要场所。它提供关于 wxWindows 的信息及对它的支持,包括下载、邮件列表、样本应用程序以及与 wxWindows 有关的工具。
- wxPython 将 Python 脚本语言同 wxWindows GUI 库结合起来。有关 wxPython 的介绍,请阅读 wxPython for newbies( developerWorks,2001 年 3 月)。
-
Coding with KParts讨论了 K 桌面环境(K Desktop Environment)图形组件的 KParts 体系结构( developerWorks,2002 年 2 月)。
- 有关更多关于 K 桌面环境的内容,请访问 KDE 主页。
- 有关更多关于 GNOME 的信息,请查看 GNOME 主页。
- 在 Red Hat 站点上可以找到更多关于 Red Hat eCos 配置工具的内容。
- 可以在 SciTech Software 站点上获取更多关于 SciTech Display Doctor的信息。
- 可以在 OS/2 Warp 页面上找到更多关于 OS/2 设备驱动程序包(它使用 SciTech Display Doctor)的信息。
- 在 developerWorksLinux 专区里可以找到 更多 Linux 文章。
关于作者
|
|
|
Markus Neifer 最初使用 LOGO 教学语言来编程,后来他使用过多种 BASIC 语言。在研究地理信息学期间,他学了一些 C,但很快又转向了 C++ 和 Java,因为这两种语言具有面向对象的本质。他曾在研发领域工作过,期间他出版过关于科技软件的面向对象开发的文章。目前,他是地理信息系统领域的软件工程师。可以通过 markus.neifer@gmx.net和 Markus 联系。
|
分享到:
相关推荐
标题“Linux下MFC的CString源码移植”表明我们需要将MFC中的CString类的功能实现移植到Linux环境,以便在不依赖MFC的情况下,能在Linux下使用类似的功能。 **CString与std::string的区别** 1. **内存管理**:...
本项目涉及到将wxWidgets的多语言功能移植到MFC动态库DLL中,这是一个涉及到C++编程、多语言支持、动态链接库(DLL)以及不同库间集成的技术挑战。 首先,wxWidgets的多语言支持主要依赖于`wxLocale`类,它允许程序...
在IT领域,将应用程序从一个操作系统移植到另一个操作系统是一项技术挑战,尤其是当源环境(如Microsoft Visual Studio 2005)与目标环境(如Linux)存在显著差异时。本知识点详细探讨了从VS2005开发的Windows程序向...
开发人员在将Windows环境下的Microsoft Foundation Classes (MFC)应用程序移植到Linux时,会面临一系列技术和策略上的挑战。本文旨在为这些开发者提供一些思路和指导,帮助他们在保持原有MFC应用的基础上,支持Linux...
在标题"template_Custom_MFC_MFC数据类库跨平台移植_"中提到的“跨平台移植”是指将基于MFC的数据类库应用到非Windows操作系统,如Linux。 **cstring**: MFC中的cstring类是对C++标准库中的std::string的一个扩展,...
通过以上步骤,你就可以成功地将wxWidgets的多语言机制移植到MFC应用中。这种方法虽然需要一定的工作量,但它允许你利用wxWidgets的成熟多语言支持,为MFC应用带来丰富的本地化体验。 **注意事项** 1. 要注意处理...
2. **QT的程序移植**:这一部分涉及到的是如何将QT应用程序移植到嵌入式设备上。这不仅包括编译应用程序,还需要解决依赖关系、设置环境变量等。 #### 三、库文件移植详解 库文件移植主要包括以下几个步骤: 1. *...
7. 应用程序开发:使用Qt Creator创建和编译GUI应用程序,然后将它们部署到开发板上运行。 在开发板上运行Qt应用程序时,需要注意优化性能,因为嵌入式设备通常资源有限。这可能涉及到调整Qt的设置,如启用硬件加速...
MFC(Microsoft Foundation Classes)是微软提供的一套C++库,用于简化Windows应用程序开发,尤其在Visual C++环境中。这个“MFC绘图软件”是一个由大二学生编写的项目,利用MFC库实现了图形绘制功能。下面我们将...
应用程序的集合通常涉及到文件路径管理和程序启动。在C++中,可以使用标准库中的`<filesystem>`(在C++17及以上版本)或旧的`<fstream>`来处理文件和目录操作。开发者可能会创建一个数据库或者文本文件来存储每个...
mfc90.dll是Microsoft Foundation Classes (MFC) 库的一个重要组件,主要用于Windows应用程序开发。然而,当我们看到"mfc90.dll.rar"这样的文件时,可能会疑惑它如何与“安卓”这一标签关联起来。实际上,在安卓平台...
Microsoft Foundation Classes(简称 MFC),是 Microsoft 为 C++ 开发者提供的一个应用程序框架,主要用于简化 Windows GUI 程序的开发工作。MFC 包含了大量的类库,这些类库实现了常见的窗口控件、文档视图架构等...
【Windows和Linux动态库】 动态链接库(Dynamic Link Library, DLL)技术是程序设计中广泛使用的技巧,旨在减小程序体积,节省存储...通过理解这些差异,可以有效地进行动态库的移植和管理,从而构建跨平台的应用程序。
MinGW,全称为 Minimalist GNU for Windows,是一个开源项目,旨在将GNU开发工具集(GCC,GNU Compiler Collection)移植到Microsoft Windows操作系统上。这个项目的主要目的是为Windows开发者提供一个自由、免费的...
3. **拷贝DLL和.lib文件**:将DLL和对应的.lib文件复制到应用程序的运行目录,或者添加到系统的PATH环境变量中,以便程序运行时能正确找到它们。 4. **动态加载和调用**:使用Windows API函数如`LoadLibrary`加载...
总的来说,"PortLinux.zip"提供的资源是一个全面的移植套件,涵盖了从源代码到文档和自动化脚本的所有方面,有助于开发者有效地将VC项目迁移到Linux环境,利用g++进行编译。通过理解并应用这些资源,开发者可以提升...
除了介绍Visual C++系列工具和基础类库(MFC)外,文档还探讨了如何通过这些工具来实现跨平台应用程序的开发,强调了代码移植的重要性。它提到,编写代码时,开发者可以充分考虑到代码在不同平台上的兼容性,从而...
- Linux端客户机则采用C语言编写通信程序,C语言的简洁性和可移植性使得它成为嵌入式环境下的理想选择。 2. 嵌入式Linux介绍 - 嵌入式Linux是一种轻量级操作系统,支持多用户、多进程和多线程,具有良好的实时...