MFC学习记录--从win32程序到MFC

一.从windows程序开始,以消息为基础,以事件驱动之的win32程序;c++

windows程序依靠外部事件驱动来运行,操做系统捕捉消息,放入应用程序的消息队列,应用程序从消息队列中取出消息,针对不一样的消息采起不一样的行为做为响应。应用程序要完成某些功能,都是以函数调用形式实现,以函数调用通知操做系统来完成指定的功能,如输入输出。操做系统所能完成的每个功能都有一个特殊的函数与之对应。操做系统把它所能完成的功能封装成函数供操做系统调用,这些函数的集合就是windows操做系统提供给应用程序的编程接口,就是windows api函数。编程

具体到windows程序,消息是一个名为MSG的数据结构,封装了一个windows消息的各类属性。接受消息的主角是应用程序窗口,每个窗口都应该有一个对应的窗口处理函数,负责处理由窗口接收的不一样消息。windows

盗了一幅图来讲明windows程序的消息驱动机制:api

图1.windows应用程序消息驱动机制数据结构

 消息的结构:并发

 1 typedef struct {
 2   HWND   hwnd;       /*Handle to the window whose window procedure receives the message. 
 3                       hwnd is NULL when the message is a thread message. */
 4   UINT   message;    /*Specifies the message identifier. Applications can only use the 
 5                       low word; the high word is reserved by the system*/
 6   WPARAM wParam;    /*Specifies additional information about the message. The exact 
 7                       meaning depends on the value of the message member*/
 8   LPARAM lParam;    /*Specifies additional information about the message. The exact 
 9                       meaning depends on the value of the message member*/
10   DWORD  time;        /*Specifies the time at which the message was posted. */
11   POINT  pt;        /*Specifies the cursor position, in screen coordinates, when the message was posted. */
12 } MSG, *PMSG;

 能够用一个简单的程序来模拟windows的消息机制:app

  1 #include <windows.h>
  2 #include "resource.h"
  3 #include "generic.h"
  4 
  5 HINSTANCE _hInst;
  6 HWND _hWnd;
  7 
  8 char _szAppName[] = "Generic";
  9 char _szTitle[] ="Generic Sample Application"; 
 10 
 11 /*
 12 *The WinMain function is the conventional name for the user-provided entry point 
 13 *for a Windows-based application.
 14 *@@Param:
 15 *@HINSTANCE hInstance :Handle to the current instance of the application.
 16 *
 17 *@HINSTANCE hPrevInstance:Handle to the previous instance of the application. This 
 18 *parameter is always NULL. 
 19 *If you need to detect whether another instance already exists, create a uniquely 
 20 *named mutex using the CreateMutex function. CreateMutex will succeed even if the 
 21 *mutex already exists, but the function will return ERROR_ALREADY_EXISTS. 
 22 *This indicates that another instance of your application exists, because it created 
 23 *the mutex first. However, a malicious user can create this mutex before you do and 
 24 *prevent your application from starting. To prevent this situation, create a randomly 
 25 *named mutex and store the name so that it can only be obtained by an authorized user. 
 26 *Alternatively, you can use a file for this purpose. To limit your application to one 
 27 *instance per user, create a locked file in the user's profile directory. 
 28 *
 29 *@LPSTR lpCmdLine:Pointer to a null-terminated string specifying the command line for 
 30 *the application, excluding the program name. To retrieve the entire command line, use 
 31 *the GetCommandLine function. 
 32 *
 33 *@int nCmdShow:Specifies how the window is to be shown. This parameter can be one of the following values.
 34 *SW_HIDE:     Hides the window and activates another window.
 35 *SW_MAXIMIZE: Maximizes the specified window.
 36 *SW_MINIMIZE: Minimizes the specified window and activates the next top-level window in the Z order.
 37 *SW_RESTORE: Activates and displays the window. If the window is minimized or maximized, the system 
 38 *              restores it to its original size and position. An application should specify this flag 
 39 *              when restoring a minimized window.
 40 *SW_SHOW:     Activates the window and displays it in its current size and position.
 41 *SW_SHOWMAXIMIZED:Activates the window and displays it as a maximized window.
 42 *SW_SHOWMINIMIZED:Activates the window and displays it as a minimized window.
 43 *SW_SHOWMINNOACTIVE:Displays the window as a minimized window. This value is similar to SW_SHOWMINIMIZED, 
 44 *                    except the window is not activated.
 45 *SW_SHOWNA:   Displays the window in its current size and position. This value is similar to SW_SHOW, 
 46 *              except the window is not activated.
 47 *SW_SHOWNOACTIVATE:Displays a window in its most recent size and position. This value is similar to 
 48 *                   SW_SHOWNORMAL, except the window is not actived.
 49 *SW_SHOWNORMAL:Activates and displays a window. If the window is minimized or maximized, the system 
 50 *               restores it to its original size and position. An application should specify this flag 
 51 *               when displaying the window for the first time.
 52 */
 53 int CALLBACK winMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){
 54     MSG msg;
 55 
 56     UNREFERENCED_PARAMETER(lpCmdLine);
 57     if(!hPrevInstance)//若是没有父实例进程,初始化应用:定义窗口,并注册。只执行一次
 58         if(!InitApplication(hInstance))
 59             return (FALSE);
 60 
 61     if(!InitInstance(hInstance,nCmdShow))//为每一实例建立建立窗口;
 62         return (FALSE);
 63 
 64     while(GetMessage(&msg,NLL,0,0)){//开启消息循环
 65         TranslateMessage(&msg);
 66         DispatchMessage(&msg);
 67     }
 68 
 69     return (msg.wParam);
 70 }
 71 /*
 72 InitApplication函数中定义一个窗口,其属性与行为,并完成注册;
 73 一旦定义并注册好一个窗口,能够被以后多个应用程序实例使用;
 74 此处提供了一个窗口模板;
 75 */
 76 BOOL InitApplication(HINSTANCE hInstance)
 77 {
 78     //定义一个窗口和其属性行为
 79     WNDCLASS wc;
 80     wc.style = CS_HREADAW | CS_VERDAW;
 81     wc.lpfnWnProc = (WNDPROC)WndProc;//此属性为窗口对应的窗口函数,赋值为一个函数指针
 82     wc.cbClsExtra = 0;
 83     wc.cbWndExtra = 0;
 84     wc.hInstance = hInstance;//实例句柄
 85     wc.hIcon = LaodIcon(hInstance,"XXXX");
 86     wc.hbrBackground = GetStockObject(WHITE_BRUSH);
 87     wc.lpszMenuName = "GenericMenu";
 88     wc.lpzeClassName = _szAppName;
 89     return (RegisterClass(&wc));
 90 }
 91 /*
 92 Initstance函数中完成窗口的建立,显示,与更新;
 93 每一个应用程序实例都应该调用此函数建立本身的窗口;
 94 */
 95 BOOL InitInstance(HINSTANCE hInstance,int nCmdShow)
 96 {
 97     _hInst = hInstance;
 98     _hWnd = CreateWindow(
 99         _szAppName,
100         _szTitle,
101         WS_OVERLAPPEDWINDOW,
102         CW_USEDEFAULT,
103         CW_USEDEFAULT,
104         CW_USEDEFAULT,
105         CW_USEDEFAULT,
106         NULL,
107         NULL,
108         hInstance,
109         NULL
110     );
111     if(!_hWnd)
112         return FALSE;
113     ShowWindow(_hWnd,nCmdShow);
114     UpdateWindow(_hWnd);
115     return TRUE;
116 }
117 /*
118 *窗口函数:处理有窗口接收到的消息,窗口的行为中枢;
119 *@@Param:
120 *@HWND hWnd:    Handle to the window.
121 *@UINT message: Specifies the message.
122 *@WPARAM wParam:Specifies additional message information. The contents of this 
123 *                parameter depend on the value of the uMsg parameter;
124 *                wParam一般是与消息有关的一个常量值,也多是窗口或控件的句柄;
125 *@LPARAM lParam:Specifies additional message information. The contents of this 
126 *                parameter depend on the value of the uMsg parameter. 
127 *                lParam一般是一个指向内存中数据的指针;
128 */
129 LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
130 {
131     int wmId,wmEvent;
132     switch (message){
133         case WM_COMMAND:
134             wmId = LOWORD(wParam);
135             wmEvent = HIWORD(wParam);
136 
137             switch(wmId){
138                 case ID_ABOUT:
139                     DialogBox(_hInst,"AboutBox",hWnd),(DLGPROC)About);
140                     break;
141                 case IDM_EXIT:
142                     DestroyWindow(hWnd);
143                     break;
144                 default:
145                     return (DefWindowProc(hWnd,message,wParam,lParam));
146             }
147             break;
148         case WM_DESTROY:
149             PostQuitMessage(0);
150             break;
151         default:
152             return (DefWindowProc(hWnd,message,wParam,lParam));
153     }
154     return 0;
155 }

 winMain函数在定义好了窗口模板,并完成注册,在InitInstance函数中为应用建立好了窗口,接下来就万事俱备,等待消息驱动,程序也进入了一个while循环,即消息循环: GetMessage函数从消息队列中抓取消息,判断是否为个人消息,若是不是将释放CPU控制权,让其余程序运行,以达到多任务协调运行。若是是本应用的消息,进入while循环体,TranslateMessage将键盘消息转化,DispatchMessage分发消息给窗口处理函数,此处只有一个消息结构体做为参数,并未指定分发方向,可是,每个消息体第一个成员就指定了接收他的窗口(这个参数的赋值由操做系统完成),而每个窗口必有惟一指定的窗口处理函数。窗口处理函数WndProc是一个CALLBACK函数,由操做系统调用;框架

以上WndProc函数对不一样消息的处理是由Switch/case实现,这样的结构一般能够重构成更精简通用的形式。暂且先弄明白win32程序的整个生命周期;dom

图2:win32程序生命周期ide

应用程序InitInstance初始化过程当中,调用CreateWindow函数,CreateWindow函数根据当前实例对应的窗口模板建立窗口,但并不显示窗口,并产生WM_CREATE消息,该消息直接发送给窗口函数而不通过消息队列;窗口函数能够在捕获此消息时知道窗口已建立,程序准备开始,所以也能够作自定义一些初始化工做;                                                                                                               

ShowWindow和UpdateWindow函数完成窗口的显示。接着整个程序进入消息循环,接收消息驱动;                                                                                                                         

GetMessage负责从消息队列中抓取消息,若是消息不是WM_QUIT,就继续循环,一旦抓取到WM_QUIT,GetMessage将返回0,while循环结束;                                                         

DispatchMessage分发消息给窗口处理函数;                                                                                                                                                                                                           

当用户出发窗口关闭按钮,操做系统将向消息队列写入WM_CLOSE消息,自定义的窗口处理函数通常不处理此类消息,所以DefWindowProc函数将负责处理全部这类消息。DefWindow接收到WM_CLOSE消息后,将调用系统的DestoryWindow API函数清除窗口,并发送WM_DESTORY消息。自定义窗口函数中对WM_DESTORY消息处理通常都是调用PostQuitMessage(0),此函数接着产生WM_QUIT消息。

重构Switch/Case形式的消息处理:

能够定义这样一种映射,包含全部的消息类型和该消息的处理例程,没接收到消息,仅仅是检索这个映射找到相应的消息处理例程,这个映射是独立于消息处理函数,所以能够随意的添加或删除消息映射项。换一种方式说,提供给窗口处理函数一个消息路由表,让窗口处理函数按消息路由表办事,窗口处理函数并不参与此路由表的建立于编辑。所以程序相对灵活;甚至,路由能够组织成多级路由表,使消息映射的查找更为敏捷;

定义这样的结构体和宏:

 1 /*消息路由表项*/
 2 struct MSGMAP_ENTRY {
 3     UINT nMessage;
 4     LONG (*pfn)(HWND,UINT,WPARAM,LPARAM);
 5 };
 6 /*取得路由表大小*/
 7 #define dim(x) (szieof(x) / sizeof(x[0]));
 8 
 9 /*建立路由表*/
10 struct MSGMAP_ENTRY _messageEntries[]=
11  {
12     /* data */
13     WM_CREATE,OnCreate,
14     WM_PAINT,OnPaint,
15     WM_SIZE,OnSize,
16     WM_COMMAND,OnCommand,
17     WM_SETFOCUS,OnSetFocus,
18     WM_CLOSE,OnClose,
19     WM_DESTROY,OnDestroy,
20 };
21 /*命令路由子表*/
22 struct MSGMAP_ENTRY _commandEntries[]=
23 {
24     /* data */
25     IDM_ABOUT,OnAbout,
26     IDM_FILEOPEN,OnFileOpen,
27     IDM_SAVES,OnSaveAs,
28 };

 窗口处理函数能够重构为:

 1 LRESULT CALLBACK WndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
 2 {
 3     int i = 0;
 4     //简单检索路由表,找到相应的处理例程
 5     for(i = 0;i<dim(_messageEntries);i++){
 6         if(message == _messageEntries[i].nMessage)
 7             return ((*_messageEntries[i].pfn)(hwnd,message,wParam,lParam));
 8     }
 9     return (DefWindowProc(hwnd,message,wParam,lParam));
10 }
11 
12 //WM_COMMAND命令交给OnCommand函数例程,OnCommand将继续查询路由子表
13 LONG OnCommand(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
14 {
15     int i = 0;
16     for(i = 0;i<dim(_commandEntries);i++){
17         if(LOWORD(wParam) == _commandEntries[i].nMessage)
18             return ((*_commandEntries[i].pfn)(hwnd,message,wParam,lParam));
19     }
20     return (DefWindowProc(hwnd,message,wParam,lParam));
21 }
22 
23 LONG OnCreate(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){}
24 LONG OnSize(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){}
25 //...

 程序空闲时怎么办?

多任务系统中常常是并发多个程序,有很大几率在很长一段时间内某一应用程序在消息对列中并无属于他的消息,此时GetMessage若是未能从消息队列中抓取消息,程序的主执行线程(注意这个词:主执行线程)会被操做系统虚悬,让出CPU控制权,转而执行其余程序。一段时间后,应用程序再次得到CPU,若是此时仍然没有消息,GetMessage继续让出CPU。

PeekMessage与GetMessag负责相同的任务,但具备不一样的表现,主要体如今,当程序从新得到CPU,PeekMessage没法得到此应用的消息时,并不当即让出CPU,而是进入OnIdle函数运行空闲是程序任务;使用PeekMessage的消息循环:

 1 while(TRUE){
 2     if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){
 3         if(msg.message == WM_QUIT)
 4             break;
 5         TranslateMessage(&msg);
 6         DispatchMessage(&msg);
 7     }
 8     else{
 9         OnIdle();
10     }
11 }

 二.MFC过渡,一个简洁的类MFC程序

MFC程序也是Windows程序,也必须遵循windows程序的机制,MFC只是在windows程序的标准之上,封装了一些经常使用操做,重构为一些类和接口函数,方便开发和调用。MFC称为一套Application Framework。略去一些必要的头文件,从剖析一个简单的MFC程序缩影开始整理MFC逻辑。

 

 1 /*HELLO.H*/
 2 class CMyWinApp:public CMyWinApp
 3 {
 4 public:
 5     BOOL InitInstance();
 6 };
 7 
 8 class CMyFrameWnd:public CFrameWnd
 9 {
10 public:
11     CMyFrameWnd();
12     afx_msg void OnPaint();
13     afx_msg void OnAbout();
14 
15 private:
16     DECLARE_MESSAGE_MAP()
17     static VOID CALLBACK someoperate();
18 }
19 
20 /*HELLO.CPP*/
21 #include <afxwin.h>
22 #include <hello.h>
23 
24 CMyWinApp theApp;
25 
26 BOOL CMyWinApp::InitInstance()
27 {
28     m_pMainWnd = new CMyFrameWnd();
29     m_pMainWnd->ShowWindow(m_nCmdShow);
30     m_pMainWnd->UpdateWindow();
31     return TRUE;
32 }
33 CMyFrameWnd::CMyFrameWnd()
34 {
35     Create(NULL,"Hello,MFC",WS_OVERLAPPEDWINDOW,rectDedault,NULL,"MainMenu");
36 }
37 
38 BEGIN_MESSAGE_MAP(CMyFrameWnd,CFrameWnd)
39     ON_COMMAND(IDM_ABOUT,OnAbout)
40     ON_WM_PAINT()
41 END_MESSAGE_MAP()
42 
43 void CMyFrameWnd::OnPaint()
44 {
45     /**/
46 }

 

这是一个可运行的“hello world ”MFC程序的主框架,修剪掉一些控件和具体消息处理例程,就只用到了两个主要类,CWinApp和CFrameWnd,以及他们的衍生类。程序自己已经包含了Windows程序的生命周期函数,但仅有以上两个类还没法窥得MFC程序的执行流程,是由于MFC即成为一个优秀的application framew,对windows程序进行了重构,使其面向对象,面向接口调用。

从哪里开始?

首先,程序没有如win32程序那样的有一个winMain函数做为入口点,但一个可执行程序一定会有一个入口函数接收标准输入参数以启动整个程序,在HELLO.CPP文件中,在大多数类函数外,实例化了一个CMyWinApp类对象theApp,这个对象做为全局对象,在任何函数被调用前被建立,将调用类构造函数;CMyWinApp继承自CWinApp,MFC中称CWinApp的衍生对象为application object,表明了一个应用程序实例(这句话要好好体会),CWinApp在头文件AFXWIN.H中定义,从其节选部分代码:

 1 class CWinApp : public CWinThread
 2 {
 3     //Attributes windows程序中由操做系统传递给winMain函数的四个参数
 4     HINSTANCE m_hInstance;
 5     HINSTANCE m_PrevInstance;
 6     LPSTR m_lpCmdLine;
 7     int m_nCmdShow;
 8     
 9     //Runing args
10     LPCTSTR m_pszAppName;    //human readable name
11     LPCTSTR m_pszRegistryKey;//userd for registry entries
12 
13 public:
14     LPCTSTR m_pszExeName;
15     LPCTSTR m_pszHelpFilePath;
16     LPCTSTR m_pszProfileName;
17 
18 public:
19     virtual BOOL InitApplication();
20 
21     virtual BOOL InitInstance();
22     virtual int ExitInstance();
23     virtual int Run();
24     virtual BOOL OnIdle();
25 }

CWinApp类中定义了四个成员变量,正是windows程序中有操做系统传递给winMain函数四个参数,而且,winMain函数通常应该调用的InitApplication,InitInstance也是CWinApp的成员函数,并都提供成虚拟函数,以供衍生类复写。能够说CWinApp完成了winMain函数所要完成的任务,InitApplication中定义窗口模板并注册,IintInstance函数中建立窗口;一切从theApp对象的建立开始,调用类构造函数,构造函数中完成一些线程初始化等操做,以后对象被建立;

CWinApp构造函数仅仅是建立了表明应用程序的CWinApp对象,并无调用InitApplication或InitInstance函数初始化和建立窗口,那么这些必要的起始任务在哪里完成?可是,做为应用程序的化身,从CWinApp继承而来的CMyWinApp中已经复写好了InitApplication和InitInstance动做,而且第一步已经建立获得了CMyWinApp全局对象,如今只等对象调用这两个动做就可完成全部的应用建立任务。

什么时候调用?

做为application object对象的theApp配置完成后,MFC程序的”winMain函数“函数开始发挥做用,这个所谓的winMain函数并不是由咱们所写,而是在MFC中已经定义好了,MFC遵循了一套标准,程序建立一定调用InitApplication和InitInstance,因此MFC已经提早写好了Main函数:

 1 //精简后
 2 int  AFXAPI AfxWinMain (HINSTANCE hInstance,HINSTANCE hPrevInstance,LPCTSTR lpCmdLine,LPCTSTR nCmdShow)
 3 {
 4     int nReturnCode = -1;
 5     CWinApp* pApp = AfxGetApp();
 6 
 7     AfxWinInit(hInstance,hPrevInstance,lpCmdLine,nCmdShow);
 8 
 9     pApp->InitApplication();
10     pApp->Initstance();
11     nReturnCode = pApp->Run();
12 
13     AfxWinTerm();
14     return nReturnCode;
15 }

Afx开头的函数都是MFC定义的全局函数,不属于任何类,AfxGetApp函数获取一开始建立的application object对象theApp,AfxWinInit完成内部初始化,应该与系统有关,先跳过。接着调用了theApp对象的两个成员函数建立应用,再就调用Run函数展开消息循环。

通常不复写InitApplication,让程序调用CWinApp中由MFC定义的InitApplication,这里完成一些内部管理初始化操做(关于Document等)。

有一个问题:想一想windows程序中InitApplication和InitInstance函数各完成了什么任务?

Windows程序中winMain函数调用InitApplication函数完成了窗口模板的定义,并注册,InitInstance函数中以窗口模板建立了窗口,并绑定了窗口函数,最后显示窗口,等待消息循环,应用程序正式登上历史舞台。MFC程序中这两个函数是否也是如此?

有一点点不一样,MFC做为一个完整的application framework,有复杂完整的结构(暂时这么以为),在InitApplication函数中并无建立或注册WNDCLASS的窗口模板,全部这些操做彷佛都在InitInstance中完成。事实上咱们无需定义窗口模板,MFC会为咱们配置一个标准模板,并注册,咱们要作的也许只是建立这样的窗口时添加一些“个性化”的渲染,以使其“不同凡响”;这些操做在哪里完成,InitInstance中吗?其实还有一个类CFrameWnd,看一下CMyWinAp::InitInstance函数,这是一个复写的虚函数,能够自定义初始化窗口过程,在HELLO.APP中能够看到,继续盗图来讲明过程:

 

CFrameWnd相似乎是窗口类,集窗口的建立,显示,接收处理窗口函数为一体,用MFC中的话,“CFrameWnd主要用来掌握一个窗口”,CFrameWnd又是怎样登上舞台的?

由CWinAapp全局对象的建立和AfxWinMain函数调用,已经为程序搭好了后台环境,而且theApp对象也为应用“圈好了地”(准备好了进程,分配了内存),就等着应用程序真正运行起来有所做为,还缺什么,至少缺两个主体,虽然有了程序运行的fundation,但windows程序是以消息驱动为生命的,必须至少有一个接收消息(系统消息和用户消息)的主体,和一个处理消息的主体,说CWinApp对象表明了整个应用,那应该有这样两个主体的体现;上面有提到CFrameWnd掌握了一个窗口,集窗口的定义显示与行为为一体,那么CWinApp只需体现CFrameWnd就能够,确实如此,CWinApp有一个成员变量,就是用来描述CFrameWnd,InitInstance函数中new了一个CFrameWnd对象,并把其赋值为CWinApp的m_pMainWnd成员变量,随着CFrameWnd对象的诞生,应用程序小世界又发生了巨大的变化。

CFrameWnd构造了什么?

CFrameWnd继承自CWnd,程序中定义了一个衍生类继承CFrameWnd,构造函数中调用了Create方法,衍生类CMyFrameWnd并无定义Create方法,Create方法是CFrameWnd的一个成员函数(非虚,并不是继承自CWnd),共八个参数,函数原型以下:

 1 BOOL CFrameWnd::Create(LPCTSTR lpszClassName,
 2     LPCTSTR lpszWindowName,
 3     DWORD dwStyle,
 4     const RECT& rect,
 5     CWnd* pParentWnd,
 6     LPCTSTR lpszMenuName,
 7     DWORD dwExStyle,
 8     CCreateContext* pContext)
 9 {
10     HMENU hMenu = NULL;
11     if (lpszMenuName != NULL)
12     {
13         // load in a menu that will get destroyed when window gets destroyed
14         HINSTANCE hInst = AfxFindResourceHandle(lpszMenuName, RT_MENU);
15         if ((hMenu = ::LoadMenu(hInst, lpszMenuName)) == NULL)
16         {
17             TRACE0("Warning: failed to load menu for CFrameWnd.\n");
18             PostNcDestroy();            // perhaps delete the C++ object
19             return FALSE;
20         }
21     }
22 
23     m_strTitle = lpszWindowName;    // save title for later
24 
25     if (!CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle,
26         rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
27         pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext))
28     {
29         TRACE0("Warning: failed to create CFrameWnd.\n");
30         if (hMenu != NULL)
31             DestroyMenu(hMenu);
32         return FALSE;
33     }
34 
35     return TRUE;
36 }

 

Create函数的八个参数:
///////////////////////////////////////////////////////////

Create函数旨在建立一个窗口,但从windows api程序中推断,窗口的产生过程应是先定义窗口模板,注册窗口,最后建立,显示。此处Create要建立窗口,亦不能无中生有,建立以前一定也需定义注册窗口类别。

Create函数中调用CreateEx函数来完成其主要功能,CFrameWnd及其衍生类中并无从新定义CreateEx函数,因此此处调用的是父类CWnd::CreateEx函数。函数原型以下:

 1 BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,
 2     LPCTSTR lpszWindowName, DWORD dwStyle,
 3     int x, int y, int nWidth, int nHeight,
 4     HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam)
 5 {
 6     // allow modification of several common create parameters
 7     CREATESTRUCT cs;
 8     cs.dwExStyle = dwExStyle;
 9     cs.lpszClass = lpszClassName;
10     cs.lpszName = lpszWindowName;
11     cs.style = dwStyle;
12     cs.x = x;
13     cs.y = y;
14     cs.cx = nWidth;
15     cs.cy = nHeight;
16     cs.hwndParent = hWndParent;
17     cs.hMenu = nIDorHMenu;
18     cs.hInstance = AfxGetInstanceHandle();
19     cs.lpCreateParams = lpParam;
20 
21     if (!PreCreateWindow(cs))
22     {
23         PostNcDestroy();
24         return FALSE;
25     }
26 
27     AfxHookWindowCreate(this);
28     HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
29             cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
30             cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);
31 
32 #ifdef _DEBUG
33     if (hWnd == NULL)
34     {
35         TRACE1("Warning: Window creation failed: GetLastError returns 0x%8.8X\n",
36             GetLastError());
37     }
38 #endif
39 
40     if (!AfxUnhookWindowCreate())
41         PostNcDestroy();        // cleanup if CreateWindowEx fails too soon
42 
43     if (hWnd == NULL)
44         return FALSE;
45     ASSERT(hWnd == m_hWnd); // should have been set in send msg hook
46     return TRUE;
47 }

 CreateEx根据传入参数初始化了一个CREATESTRUCT对象cs,CREATESTRUCT是一个结构体,在winuser.h头文件中定义,定义此结构体的目的在于封装一个窗口应用函数的全部参数,封装好的“参数结构体”被看成PreCreateWindow的实参传递,PreCreateWindow又是何函数?

PreCreateWindow是一个虚函数,CWnd和CFrameWnd中都有实现,调用时基于c++的多态模式。该函数的目的是在窗口建立以前作预处理,函数定义以下:

 1 BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs)
 2 {
 3     if (cs.lpszClass == NULL)
 4     {
 5         VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));
 6         cs.lpszClass = _afxWndFrameOrView;  // COLOR_WINDOW background
 7     }
 8 
 9     if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4)
10         cs.style |= FWS_PREFIXTITLE;
11 
12     if (afxData.bWin4)
13         cs.dwExStyle |= WS_EX_CLIENTEDGE;
14 
15     return TRUE;
16 }

 参数是一个CREATESTRUCT结构体的一个引用,在函数内能够修改cs对象的属性,那意味着函数中能够修改CWnd::CreateEx函数中对窗口参数信息的预约义,若是自定义的类复写此函数,将能够随意定制窗口的属性。函数体中AfxDeferRegisterClass是一个定义在AFXIMPL.H中的宏,接受一个AFX_WNDFRAMEORVIEW_REG的窗口类型,检测其是否被注册并调用注册功能

1 #define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)

 

AfxEndDeferRegisterClass是定义在wincore.cpp中的一个全局函数,完成对指定窗口类别的注册。从CWnd衍生的各种mfc使用不一样的窗口类型注册。当注册成功,CreateWindowEx建立窗口并返回句柄。

窗口建立完成,showWindow和UpdateWindow显示更新窗口,最后调用run函数程序开始消息循环,由消息驱动运行起来。

MFC中CFrameWnd的一些类的生命过程函数与此会有点诧异,也许不会再构造函数中调用Create函数,会有一个LoadFrame函数来调用Create,接下来的过程都相同,可类比,LoadFrame完成的更多一些窗口的初始化功能。

相关文章
相关标签/搜索