wx網羅系列之翔實:將 MFC 應用程式移植到 Linux

來源:互聯網
上載者:User

 此文將MFC與wxWidgets做了方方面面的比照,尤其是其類階層和一些關鍵性的宏,為MFC程式員提供了一個向wxWidgets移植的入門指南。排版整理完畢。

您可能仍然在維護用微軟基礎類庫(Microsoft Foundation Classes(MFC))構建的舊的 Windows 應用程式,而現在卻有客戶要求 Linux 版本,該怎麼辦呢?在您的團隊中可能有技術熟練的 MFC 開發人員,但如何達到加速 Linux 開發呢?別急;本文就是針對您這種情況而寫的。依靠 wxWindows(一種用於 C++ 和 Python 的可移植 GUI 工具箱)的協助,我將以多重文件介面(Multiple Document Interface (MDI))文字編輯器為例向您示範如何將僅 Windows 的 MFC 應用程式移植到 Linux。類似這樣的小型應用程式有助於我們將討論集中在移植架構的具體細節上,從而避免我們迷失在代碼的汪洋中。可以在本文後面的 參考資料一節中擷取完整的 MFC 應用程式和 wxWindows 應用程式的原始碼。

文檔/視圖概述

我將示範的應用程式使用眾所周知的文檔/視圖體繫結構,因為它可以象大多數應用程式一樣處理文檔。即使您的應用程式不使用文檔/視圖體繫結構,我也建議您讀下去。只要您已在轉向這種架構,您就可能想要添加這項功能。

在我的《細述wxWindows》中,曾經指出過 MFC 和 wxWindows 之間具有某些相似性。字串類 CStringwxString 和事件系統之間都非常相似。但還不止這些相似性。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 所包含的應用程式嚮導來建立,但在My Code片段中,我將不給出由嚮導產生的、有時會使人迷惑的注釋( //{{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 類的聲明類似於這樣:

文檔類聲明: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 聯絡。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.