17xie > VC++深入详解 > 3.2.2 MFC框架窗口
背景:                 
[本书目录] [图书首页] [本书讨论区]  
链接地址:http://www.17xie.com/read-36306.html    注册17xie 一起来写书 实现您的出书梦想!

3.2.2  MFC框架窗口

1.设计和注册窗口

有了WinMain函数,根据创建Win32应用程序的步骤,接下来应该是设计窗口类和注册窗口类了。MFC已经为我们预定义了一些默认的标准窗口类,只需要选择所需的窗口类,然后注册就可以了。窗口类的注册是由AfxEndDeferRegisterClass函数完成的,该函数的定义位于WINCORE.CPP文件中。其定义代码较长,由于篇幅所限,在这里仅列出部分代码,如例3-10所示。

3-10

BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)

{

……

    // common initialization

    WNDCLASS wndcls;

    memset(&wndcls, 0, sizeof(WNDCLASS));   // start with NULL defaults

  wndcls.lpfnWndProc = DefWindowProc;

    wndcls.hInstance = AfxGetInstanceHandle();

    wndcls.hCursor = afxData.hcurArrow;

……

    // work to register classes as specified by fToRegister, populate fRegisteredClasses as we go

    if (fToRegister & AFX_WND_REG)

    {

        // Child windows - no brush, no icon, safest default class styles

        wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;

        wndcls.lpszClassName = _afxWnd;

        if (AfxRegisterClass(&wndcls))

            fRegisteredClasses |= AFX_WND_REG;

    }

    if (fToRegister & AFX_WNDOLECONTROL_REG)

    {

        // OLE Control windows - use parent DC for speed

        wndcls.style |= CS_PARENTDC | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;

        wndcls.lpszClassName = _afxWndOleControl;

        if (AfxRegisterClass(&wndcls))

            fRegisteredClasses |= AFX_WNDOLECONTROL_REG;

    }

……

    if (fToRegister & AFX_WNDMDIFRAME_REG)

    {

        // MDI Frame window (also used for splitter window)

        wndcls.style = CS_DBLCLKS;

        wndcls.hbrBackground = NULL;

        if (_AfxRegisterWithIcon(&wndcls, _afxWndMDIFrame, AFX_IDI_STD_ MDIFRAME))

            fRegisteredClasses |= AFX_WNDMDIFRAME_REG;

    }

    if (fToRegister & AFX_WNDFRAMEORVIEW_REG)

    {

        // SDI Frame or MDI Child windows or views - normal colors

        wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;

        wndcls.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);

        if (_AfxRegisterWithIcon(&wndcls, _afxWndFrameOrView, AFX_IDI_STD _FRAME))

            fRegisteredClasses |= AFX_WNDFRAMEORVIEW_REG;

    }

……

}

从例3-10所示代码可知,AfxEndDeferRegisterClass函数首先判断窗口类的类型,然后赋予其相应的类名(wndcls.lpszClassName变量),这些类名都是MFC预定义的。之后就调用AfxRegisterClass函数注册窗口类,后者的定义也位于WINCORE.CPP文件中,代码如例3-11所示。

3-11

BOOL AFXAPI AfxRegisterClass(WNDCLASS* lpWndClass)

{

    WNDCLASS wndcls;

    if (GetClassInfo(lpWndClass->hInstance, lpWndClass->lpszClassName,

        &wndcls))

    {

        // class already registered

        return TRUE;

    }

 

    if (!::RegisterClass(lpWndClass))

    {

        TRACE1("Can't register window class named %s\n",

            lpWndClass->lpszClassName);

        return FALSE;

    }

 

    if (afxContextIsDLL)

    {

        AfxLockGlobals(CRIT_REGCLASSLIST);

        TRY

        {

            // class registered successfully, add to registered list

            AFX_MODULE_STATE* pModuleState = AfxGetModuleState();

            LPTSTR lpszUnregisterList = pModuleState->m_szUnregisterList;

            // the buffer is of fixed size -- ensure that it does not overflow

            ASSERT(lstrlen(lpszUnregisterList) + 1 +

                lstrlen(lpWndClass->lpszClassName) + 1 <

                _countof(pModuleState->m_szUnregisterList));

            // append classname + newline to m_szUnregisterList

            lstrcat(lpszUnregisterList, lpWndClass->lpszClassName);

            TCHAR szTemp[2];

            szTemp[0] = '\n';

            szTemp[1] = '\0';

            lstrcat(lpszUnregisterList, szTemp);

        }

        CATCH_ALL(e)

        {

            AfxUnlockGlobals(CRIT_REGCLASSLIST);

            THROW_LAST();

            // Note: DELETE_EXCEPTION not required.

        }

        END_CATCH_ALL

        AfxUnlockGlobals(CRIT_REGCLASSLIST);

    }

 

    return TRUE;

}

3-11所示代码可知,AfxRegisterClass函数首先获得窗口类信息。如果该窗口类已经注册,则直接返回一个真值;如果尚未注册,就调用RegisterClass函数注册该窗口类。读者可以看出这个注册窗口类函数与第2章介绍的Win32 SDK编程中所使用的函数是一样的。

小技巧:如果在当前工程文件中查找某个函数或字符串,可以利用工具栏上的“Find in Files”工具按钮或Edit菜单下的Find in Files命令;如果在当前文件中查找某个函数或字符串,可以使用Ctrl+F快捷键或Edit菜单下的Find命令。

我们创建的这个MFC应用程序Test,实际上有两个窗口。其中一个是CMainFrame类的对象所代表的应用程序框架窗口。该类有一个PreCreateWindow函数,这是在窗口产生之前被调用的。该函数的默认实现代码如例3-12所示。

3-12

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)

{

    if( !CFrameWnd::PreCreateWindow(cs) )

        return FALSE;

    // TODO: Modify the Window class or styles here by modifying

    //  the CREATESTRUCT cs

 

    return TRUE;

}

从其代码可知,该函数首先调用CFrameWndPreCreateWindow函数。后者的定义位于源文件WINFRM.CPP中,代码如例3-13所示。

3-13

BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)

{

    if (cs.lpszClass == NULL)

    {

        VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));

        cs.lpszClass = _afxWndFrameOrView;  // COLOR_WINDOW background

    }

 

    if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4)

        cs.style |= FWS_PREFIXTITLE;

 

    if (afxData.bWin4)

        cs.dwExStyle |= WS_EX_CLIENTEDGE;

 

    return TRUE;

}

我们发现该函数中调用了AfxDeferRegisterClass函数,读者可以在AFXIMPL.H文件中找到后者的定义,定义代码如下:

#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)

由其定义代码可以发现,AfxDeferRegisterClass实际上是一个宏,真正指向的是AfxEndDefer-RegisterClass函数。根据前面介绍的内容,我们知道这里完成的功能就是注册窗口类。

CMainFrame类的PreCreateWindow函数处设置一个断点,调试运行Test程序,将会发现程序在调用theApp全局对象和WinMain函数之后,到达此函数处。由此,我们知道MFC程序执行的脉络也是在WinMain函数之后,窗口产生之前注册窗口类的。

2.创建窗口

按照Win32程序编写步骤,设计窗口类并注册窗口类之后,应该是创建窗口了。在MFC程序中,窗口的创建功能是由CWnd类的CreateEx函数实现的,该函数的声明位于AFXWin.h文件中,具体代码如下所示。

BOOL CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,

    LPCTSTR lpszWindowName, DWORD dwStyle,

    int x, int y, int nWidth, int nHeight,

    HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam = NULL);

其实现代码位于WINCORE.CPP文件中,部分代码如例3-14所示。

3-14

BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,

    LPCTSTR lpszWindowName, DWORD dwStyle,

    int x, int y, int nWidth, int nHeight,

    HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)

{

    // allow modification of several common create parameters

    CREATESTRUCT cs;

    cs.dwExStyle = dwExStyle;

    cs.lpszClass = lpszClassName;

    cs.lpszName = lpszWindowName;

    cs.style = dwStyle;

……

    if (!PreCreateWindow(cs))

    {

        PostNcDestroy();

        return FALSE;

    }

 

    AfxHookWindowCreate(this);

    HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,

        cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,

        cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);

……

}

MFC底层代码中,CFrameWnd类的Create函数内部调用了上述CreateEx函数。而前者又由CFrameWnd类的LoadFrame函数调用。读者可以自行跟踪这一调用过程。

CFrameWnd类的Create函数的声明也位于AFXWin.h文件中,具体代码如下所示。

BOOL Create(LPCTSTR lpszClassName,

            LPCTSTR lpszWindowName,

            DWORD dwStyle = WS_OVERLAPPEDWINDOW,

            const RECT& rect = rectDefault,

            CWnd* pParentWnd = NULL,        // != NULL for popups

            LPCTSTR lpszMenuName = NULL,

            DWORD dwExStyle = 0,

            CCreateContext* pContext = NULL);

其定义位于在WINFRM.CPP文件中,部分代码如例3-15所示。

3-15

BOOL CFrameWnd::Create(LPCTSTR lpszClassName,

    LPCTSTR lpszWindowName,

    DWORD dwStyle,

    const RECT& rect,

    CWnd* pParentWnd,

    LPCTSTR lpszMenuName,

    DWORD dwExStyle,

    CCreateContext* pContext)

{

……

    if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,

        rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,

        pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))

    {

        TRACE0("Warning: failed to create CFrameWnd.\n");

        if (hMenu != NULL)

            DestroyMenu(hMenu);

        return FALSE;

    }

……

}

CFrameWnd类派生于CWnd类。从上述声明代码可知,CWnd类的CreateEx函数不是虚函数。另外,CFrameWnd类中也没有重写这个函数。根据类的继承性原理,CFrameWnd类就继承了CWnd类的CreateEx函数。因此,例3-15所示CFrameWnd类的Create函数内调用的实际上就是CWnd类的CreateEx函数。读者可以在这两个函数的定义处都设置断点,然后调试运行Test程序以验证这一点。

再回到例3-14所示CWnd类的CreateEx函数实现代码中,可以发现该函数中又调用了PreCreateWindow函数,后者是一个虚函数。因此,这里实际上调用的是子类,即CMainFrame类的PreCreateWindow函数。之所以在这里再次调用这个函数,主要是为了在产生窗口之前让程序员有机会修改窗口外观,例如,去掉窗口的最大化按钮等,PreCreateWindow函数的参数就是为了实现这个功能而提供的。该参数的类型是CREATETRUCT结构,我们可以把这个结构体与CreateWindowEx函数的参数作一个比较,图3.19CREATETRUCT结构和CreateWindowEx函数声明的一个对比,注意左边结构体成员与右边函数参数的对应关系。

3.19  CREATETRUCT结构和CreateWindowEx函数定义的对比

可以发现,CREATETRUCT结构体中的字段与CreateWindowEx函数的参数是一致的,只是先后顺序相反而已。同时,可以看到PreCreateWindow函数的这个参数是引用类型。这样,在子类中对此参数所做的修改,在其基类中是可以体现出来的。再看看前面例3-14所示CWnd类的CreateEx函数代码,如果在子类的PreCreateWindow函数中修改了CREATESTRUCT结构体的值,那么,接下来调用CreateWindowEx函数时,其参数就会发生相应的改变,从而就会创建一个符合我们要求的窗口。

 知识点  MFC中后缀名为Ex的函数都是扩展函数。

3.显示窗口和更新窗口

Test程序的应用程序类(CTestApp)中有一个名为m_pMainWnd的成员变量。该变量是一个CWnd类型的指针,它保存了应用程序框架窗口对象的指针。也就是说,是指向CMainFrame对象的指针。在CTestApp类的InitInstance函数实现内部有如下两句代码。

// The one and only window has been initialized, so show and update it.

m_pMainWnd->ShowWindow(SW_SHOW);

m_pMainWnd->UpdateWindow();

这两行代码的功能是显示应用程序框架窗口和更新这个窗口。


字数:13022    最后更新:8个月以前 [03-13 21:45]happyskynet 修改
本页编辑者:happyskynet  
[前一页]:3.2.1 MFC程序中的Win…  [后一页]:3.2.3 消息循环
[在本页中加入书签] [收藏本书] [推荐本书]
  17xie论坛 > 本书讨论区 > 本页评论   (共0条)
发表评论

用户名称 匿名发表
评论内容
验证码

关于我们 | 版权声明 | 免责声明 | 诚聘英才 | 联系我们 | 合作伙伴 | 友情链接 | 广告合作 | 提交意见
Copyright © 2007 17xie.com 互联网协同写书平台 京ICP备08002671号