现在的位置: 首页 > 综合 > 正文

移植MFC程序到WINX

2013年02月06日 ⁄ 综合 ⁄ 共 6380字 ⁄ 字号 评论关闭

移植MFC程序到WINX

许式伟

2006-12-11

概要

我们知道,WINX以兼容WTL、MFC为主。这是为了以便用户从WTL、MFC转向WINX的时候,没有太大的迁移代价。

不过,需要提醒的是,WINX兼容WTL、MFC的策略是不同的。

对于WTL,WINX的策略是,使用其实现,隐蔽其原有的使用界面。WTL是高效的。所以,对于众多的WTL的官方或第三方组件,WINX策略是“拿来主义”。既然人家已经实现了,而且很高效,我干嘛不用之?

对于MFC,有两个非常重要的现实:其一,MFC是臃肿的,多数正使用MFC或者放弃使用MFC的用户对于这一点深恶痛绝(包括我)。其二,MFC的用户是C++界面库的用户群中最庞大的一支队伍。我们来看看Google趋势:

 

从Google趋势看,MFC用户群很大,但随着越来越多的替换方案出现,有缓慢衰减趋势。

WINX对于MFC的策略,是兼容其用户群已经习惯的用法。只要掌握一定的规则,我们可以发现,从MFC到WINX,是如此轻松。

本文就是介绍如何从MFC到WINX的一个指南。由于WINX目前主要关注对话框应用程序,故此,这一篇MFC程序移植指南,主要针对的是基于对话框的MFC程序。

 

基于对话框的MFC程序移植指南

我们先来看看样例:

下面,我们开始一步步移植MFC程序。

1)头文件包含及工程配置

找到所有#include MFC相关的头文件,并删除之。改为#include 以及其他WINX相关头文件。
典型地,一个MFC程序通常主要在StdAfx.h包含MFC的头文件。 这一步主要修改StdAfx.h文件。

另外,既然是WINX程序,工程配置中的Include路径当然要包含winx目录。在VC++ 6.0中,你可以这样做:Alt+F7 => C/C++ => Category: Preprocessor => Additional include directories 。详细参考“如何建立第一个WINX程序”。

2)移植从CWinApp派生的CXXXApp类

现在我们开始移植CXXXApp类。例如,本例中的CLCDMatrixApp类。

对于MFC的对话框程序,MFC的框架(FrameWork)显得非常牵强。基本上框架除了让MFC程序多了一些代码外,没有多大作用。

移植方法:删除该类。并将类的InitInstance函数,把该函数的内容挪到WinMain中,作适当调整。
不要理会InitInstance函数的返回值,对于MFC的对话框程序,它始终返回FALSE以便终止MFC框架的默认行为(坦白说,这个设计看起来非常丑陋)。

对于本例,InitInstance函数的内容为:

BOOL CLCDMatrixApp::InitInstance()
{
    AfxEnableControlContainer();

#ifdef _AFXDLL
    Enable3dControls();
#else
    Enable3dControlsStatic();    
// Call this when linking to MFC statically
#endif

    CLCDMatrixDlg dlg;
    m_pMainWnd = &dlg;
    dlg.DoModal();

    // Since the dialog has been closed, return FALSE so that we exit the
    
//  application, rather than start the application's message pump.
    return FALSE;
}

我们改为:

int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     
int       nCmdShow)
{
    InitCommonControls();
    
    CLCDMatrixDlg dlg;
    
return dlg.DoModal();
}

由于我们没有用到什么特别的控件,这里调用InitCommonControls只是未雨绸缪,并非是必须的。

3)MFC窗口类(控件)的移植

这一部分很重要,我把它写成独立的一节。见后文。

4)对话框的移植

这一部分内容较多,我们独立成一小节。见后文。

5)MFC特有的辅助类的移植

这些类主要指容器(CList/CStringList、CArray、CMap等)。它们通常由于STL中有对应功能的类,故此WINX中并不提供。

那么怎么办那?我的建议是,不要试图去修改你的源代码,把其中用到的类换为STL对应的类。而是,用STL中相应的类模拟一个同样规格的CList/CStringList等类。这样做的好处是明显的:以后你再也不怕移植其他的MFC组件时遇到使用了这些类了。(注:读者如果进行了这样的移植,感兴趣的话,可以提交代码到WINX中。

 

MFC窗口类(控件)的移植

现在我们到了本文的重点。在这里,我们将详细讨论如何移植一个独立的MFC窗口类(或称为控件)。毕竟,一个界面程序的最重要的成分,就是窗口类。

1)消息处理过程的移移植

首先,立马删除DECLARE_MESSAGE_MAP、 BEGIN_MESSAGE_MAP .. END_MESSAGE_MAP 等消息映射表(MessageMap)相关的代码。因为地球人都知道,WINX不需要这个。

然后,你需要修改消息函数原型。为了兼容,消息函数名WINX与MFC保持一致。但是消息函数的规格(原型)略有不同。这种不同你只需要了解2条简单的规则就可以完成替换。

  1. WINX的消息处理函数第一个参数永远是HWND hWnd。这是一个看似多余的参数。因为多数情况下,你可能使用m_hWnd而不是这个hWnd。之所以这样,其中原因本文暂且不做解释。
     
  2. 消息函数原型中出现的MFC特有句柄指针(如CWnd*, CDC*等),一律改为Win32原生的句柄(如HWND、HDC等)。对于出现的MFC简单类型(如CRect、CPoint、CSize等),则改为winx中相应的简单类型(如winx::CRect、winx::CPoint、winx::CSize等)。

也许大家注意到,我习惯把消息处理函数(如OnKeyDown、OnPaint等)的调用方式声明为winx_msg。但这不是强制的。你仍然可以写作afx_msg,或者干脆不写,都是正确的。WINX对消息处理函数的调用方式并无要求。

2)绘制过程(GDI)的移植

  1. AfxGetInstanceHandle()改为GetThisModule()。
  2. GDI对象。不同于MFC采用CBitmap*来表示资源句柄的方式,WINX建议你使用原生的HBITMAP(或者CBitmapHandle)来表示资源句柄(或者说不带Own权的资源对象)。例如:
    CBitmap* pOldBitmap = dcMemory.SelectObject(CBitmap::FromHandle(bmp));

    需要改为:

      HBITMAP pOldBitmap = dcMemory.SelectObject(bmp);
    或者:
      CBitmapHandle pOldBitmap 
    = dcMemory.SelectObject(bmp);

     

MFC对话框的移植

有了以上基础,现在,我们开始移植我们程序的主体:对话框。

1)对话框的基类、对话框资源等细节

  1. 首先,对话框的基类,以及对话框IDD的声明方式要改下。MFC如下:
    class CLCDMatrixDlg : public CDialog
    {
    public:
        
    enum { IDD = IDD_LCDMATRIX_DIALOG };
    };

    在WINX中,需要改为:

    class CLCDMatrixDlg : public winx::ModalDialogCLCDMatrixDlg, IDD_LCDMATRIX_DIALOG>
    {
    };

  2. 对话框资源,主要是对话框位置。在对话框资源的属性也中,勾上“Center”样式(在More Styles属性页中)。这是因为MFC中对话框在DoModal时亲自调用了CenterWindow让对话框居中。在WINX,我还是觉得,用户自己来确定是否要Center样式。

2)对话框的图标(Icon)

MFC对话框类(本例是CLCDMatrixDlg)的多处代码与对话框图标有关,它们是:

  - 构造函数。本例为CLCDMatrixDlg(CWnd* pParent = NULL);
  
- afx_msg void OnPaint();
  
- afx_msg HCURSOR OnQueryDragIcon();

而对于WINX而言,设置图标是最容易不过的事情了。所有这些代码,只需要改为一句话:

   WINX_ICON(IDR_MAINFRAME);

搞定。

3)消息处理过程的移植

同上面“MFC窗口类(控件)的移植”。

4)子控件的移植

在MFC中,子控件通常通过DDX技术与成员变量进行绑定(通过DDX_Control)。在WTL中,也存在类似的方法DDX_CONTROL。其实WTL还多了一个DDX_CONTROL_HANDLE,是对DDX_CONTROL的优化。

MFC没有DDX_CONTROL_HANDLE,但是说真的,DDX_CONTROL_HANDLE比之DDX_CONTROL轻量太多。WTL为了显得和MFC一样,但又不愿意牺牲性能,所以对于控件的DDX,它搞出了两套:DDX_CONTROL、DDX_CONTROL_HANDLE。然而,这同时给用户造成了困扰。我什么时候该用DDX_CONTROL?什么时候该用DDX_CONTROL_HANDLE?

DDX技术是不错的,尤其在存在可视化开发平台时,可以以很便捷的方式提供了界面元素与数据的绑定,从而大大简化了用户对界面控制。但是,为了避免混淆,在WINX中,我强烈建议你仅将DDX技术用于非控件变量的映射上。换句话说,不要使用DDX_CONTROL、DDX_CONTROL_HANDLE。

说白了,DDX_CONTROL就是一句SubclassDlgItem,DDX_CONTROL_HANDLE就是一句GetDlgItem。WINX倾向于你在OnInitDialog时自己去初始化控件变量好了。

以我们要移植的程序为例,MFC代码如下:

class CLCDMatrixDlg : public CDialog
{
    CMatrixStatic    m_lcddown2;
    CMatrixStatic    m_lcdleft;
    CMatrixStatic    m_lcddown;
    CMatrixStatic    m_lcdstaticsmall;
    CMatrixStatic    m_lcdstatic;

    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
};

void CLCDMatrixDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    
//{{AFX_DATA_MAP(CLCDMatrixDlg)
    DDX_Control(pDX, IDC_MATRIXDOWN2, m_lcddown2);
    DDX_Control(pDX, IDC_LCDLEFT, m_lcdleft);
    DDX_Control(pDX, IDC_MATRIXDOWN, m_lcddown);
    DDX_Control(pDX, IDC_MATRIXSMALL, m_lcdstaticsmall);
    DDX_Control(pDX, IDC_LCDSTATIC, m_lcdstatic);
    
//}}AFX_DATA_MAP
}

移植到WINX如下:

class CLCDMatrixDlg : public winx::ModalDialogCLCDMatrixDlg, IDD_LCDMATRIX_DIALOG>
{
    CMatrixStatic
*    m_lcddown2;
    CMatrixStatic
*    m_lcdleft;
    CMatrixStatic
*    m_lcddown;
    CMatrixStatic
*    m_lcdstaticsmall;
    CMatrixStatic
*    m_lcdstatic;
};

BOOL CLCDMatrixDlg::OnInitDialog(HWND hDlg, HWND hWndDefaultFocus)
{
    winx::SubclassDlgItem(&m_lcdstatic, hDlg, IDC_LCDSTATIC);
    winx::SubclassDlgItem(
&m_lcdleft, hDlg, IDC_LCDLEFT);
    winx::SubclassDlgItem(
&m_lcdstaticsmall, hDlg, IDC_MATRIXSMALL);
    winx::SubclassDlgItem(
&m_lcddown, hDlg, IDC_MATRIXDOWN);
    winx::SubclassDlgItem(
&m_lcddown2, hDlg, IDC_MATRIXDOWN2);
    ...
}

两者的本质是一样。只是WINX的方式略微高效一些(因为它是在OnInitDialog的时候进行Subclass的,不需要每次DataExchange的时候都尝试Subclass)。另外WINX明确地告诉你,这是通过Subclass技术实现的,而MFC隐瞒了这个事实而已。

5)DDX的移植

除了我们上面已经提到,不推荐使用控件的DDX外,从MFC的DDX移植到WINX是极其简单的,因为基本上只是宏名/函数名的不同而已。由于本例没有涉及DDX,这里不展开讨论。

 

附录

附带的,我们给几个MFC控件移植的样例:

这些WINX控件的使用样例:

好了,你已经清楚任何从MFC到WINX了。现在,你也来试一试吧?下面是一个MFC对话框程序。请试着去把它改为WINX程序:

 

补充说明

以上例子代码均在WINX开发包中提供。对WINX感兴趣?您可以从以下链接之一获得最新版本: 

关注WINX,请留意 winxcn.com 以及 WINX团队Blog 发布的消息。

WINX任何消息均将在这些地方第一时间进行更新。 

 

抱歉!评论已关闭.