天天看點

将 MFC 應用程式移植到 Linux

您可能仍然在維護用微軟基礎類庫(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 類

将 MFC 應用程式移植到 Linux

圖 2. wxWindows 類

将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
回頁首

應用程式

每個架構都提供一個表示應用程式本身的類。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 中,象這樣建立應用程式類的靜态對象:

CPortMeApp theApp;
      

在 wxWindows 中,則象這樣使用

IMPLEMENT_APP()

宏:

IMPLEMENT_APP(PortedApp)
      

如果對該宏所做的事情感興趣,請檢視頭檔案

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 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
回頁首

文檔

文檔儲存應用程式處理的基于檔案的資料。它負責從檔案裝入該資料,并在必要的時候将該資料儲存回檔案。該 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 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
回頁首

視圖

如果 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 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
回頁首

主架構

既然已經有了處理和顯示資料的文檔和視圖類,并且也有了處理文檔/視圖架構的應用程式類,現在需要一個主架構類來同使用者互動。同樣,兩種架構提供類似的功能,而實際實作有略微不同。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 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
回頁首

全部工作原理

移植完成了。我來回顧一下實作這一點所需做的事情。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
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
回頁首

視窗管理器

如果您對 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 應用程式

将 MFC 應用程式移植到 Linux

圖 4. Windows 上的 wxWindows 應用程式

将 MFC 應用程式移植到 Linux

圖 5. Linux/KDE 上的 wxWindows 應用程式

将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
回頁首

真實的故事

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 項目(參閱 參考資料)。

将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
回頁首

結束語

本文示範了使用 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 文章。

關于作者

将 MFC 應用程式移植到 Linux
将 MFC 應用程式移植到 Linux
Markus Neifer 最初使用 LOGO 教學語言來程式設計,後來他使用過多種 BASIC 語言。在研究地理資訊學期間,他學了一些 C,但很快又轉向了 C++ 和 Java,因為這兩種語言具有面向對象的本質。他曾在研發領域工作過,期間他出版過關于科技軟體的面向對象開發的文章。目前,他是地理資訊系統領域的軟體工程師。可以通過 [email protected]和 Markus 聯系。

繼續閱讀