3.2.1 MFC单文档大致框架
(1)MFC类继承略图
(2) MFC单文档应用程序类层次结构图
CWinApp:应用程序类,每个应用程序有且只有一个继承于CWinApp的派生类对象
CWnd:是一个通用的窗口类,用于提供Windows中的所有通用特性、对话框和控件。
(3)应用程序执行过程图
3.2.2 应用程序QWinApp的设计
//QWinApp.h文件
#include "stdAfx.h" class QWinApp { public: QWinApp(); ~QWinApp(); public: virtual BOOL InitInstance(); //提供接口,由派生类去实现窗口注册、创建等。 virtual BOOL ExitInstance(); //应用程序结束前的收尾工作,如销毁指针等。 void Run(); //消息循环 public: LPVOID m_pMainWnd; };
//QMyWinApp.h的设计
#pragma once #include "QWinApp.h" class QMyWinApp : public QWinApp { public: QMyWinApp(); ~QMyWinApp(); public: virtual BOOL InitInstance(); virtual BOOL ExitInstance(); }; extern QMyWinApp theApp;
//QMyWinApp.c
#include "QMyWinApp.h" #include "QMainFrame.h" QMyWinApp::QMyWinApp(){} QMyWinApp::~QMyWinApp(){} QMyWinApp theApp; //全局变量,先于WinMain执行,并将应用程序指针保存在g_pWinApp中。 BOOL QMyWinApp::InitInstance() { QMainFrame* pMainFrame = new QMainFrame(); assert(pMainFrame); m_pMainWnd = pMainFrame; //创建窗口 BOOL bRet = pMainFrame->CreateEx(0, _T("MyFirstClass"), _T("我的第一个窗口程序"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL); if (!bRet) { MessageBox(NULL, _T("创建窗口失败!"), _T("创建窗口"), 0); return FALSE; } pMainFrame->ShowWindow(SW_SHOW); //显示窗口 pMainFrame->UpdateWindow(); //更新窗口 return TRUE; } BOOL QMyWinApp::ExitInstance() { delete m_pMainWnd; //删除主框架窗口 m_pMainWnd = NULL; return TRUE; }
3.2.3 一般窗口类QWnd的设计
(1)回调函数:必须是全局函数,不能是类的普通成员函数。且回调函数仅仅指明了窗口的句柄(hWnd),不能得到窗口的指针。那如何封装回调函数呢?
①MFC的做法:通过hWnd到CWnd对象指针的Map映射机制,将句柄转为指针。
②我们的做法:A、创建窗口时将窗口对象的指针保存在窗口类所指定的额外数据空间中,可以通过SetWindowLong存入并在回调函数中用GetWindowLong取出来,然后转化为窗口对象的指针,即调用类的成员函数。B、原回调函数就变成一个傀儡,真正在消息处理过程被该我们定义的虚函数WindowProc给接管了。
SetWindowLong函数——将窗口对象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,this); assert(hWnd); m_hWnd =hWnd; SetWindowLong(m_hWnd,GWL_USERDATA,(LONG)this);
GetWindowLong函数及消息处理
LRESULT CALLBACK QWnd::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { QWnd* pWnd = (QWnd*)GetWindowLong(hwnd,GWL_USERDATA); //从窗口额外空间得到窗口this指针 if (pWnd) { return pWnd->WindowProc(uMsg,wParam,lParam); //消息被路由到WindowProc处理。 } return ::DefWindowProc(hwnd, uMsg, wParam, lParam); }
③WNDCLASSEX(或WNDCLASS)中的cbWndExtra成员用于指明紧跟窗口类之后额外创建的空间大小,以字节为单位。创建完之后,可以使用SetWindowLong函数来设置该额外空间的值,使用GetWindowLong来访问该空间的值。nIndex设定在0 ~ (额外空间大小- sizeof(long))之间。
GWL_USERDATA标识符:当窗口类创建完成后,会自动创建一块32位数值的空间,可供用户自己处理,该空间与由cbWndExtra创建的额外空间无关,不能用GWL_USERDATA来设置或访问由cbWndExtra创建的额外空间。
(2)WM_Create和WM_NCCreate消息
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,this); //说明:执行上面函数会发送WM_CREATE和WM_NCCREATE消息,由于执行这两条消息 //期间CreateWindowsEx尚未返回,即得不到hWnd,也就无法在消息处理过程中利用 //GetWindowLong和hWnd值取得this指针,所以必须手动在CreateWindowEx通用 //lpParam参数传入this指针。然后在消息处理过程中,对这两个消息进行特殊处理。
QWnd* pWnd = NULL; //处理CreateWindowEx过程中发送的两个消息中无法获取窗口对象指针的问题。 if (uMsg == WM_CREATE || uMsg == WM_NCCREATE) { CREATESTRUCT *pCs = (CREATESTRUCT*)lParam; if (pCs) { pWnd = (QWnd*)pCs->lpCreateParams; //窗口对象指针保存在lpCreateParams中 if (pWnd) { pWnd->m_hWnd = hWnd; return pWnd->WindowProc(uMsg, wParam, lParam); } } }
3.2.4 让QWnd支持标准控件
(1)标准控件的窗口回调函数是操作系统注册的,并提供给用户。因回调函数不是我们指定的。消息便不能路由到我们所提供的WndProc中进行处理。
(2)标准控件的子类化——创建窗口后,采用子类化的机制用我们回调函数替换原先系统提供的窗口回调函数。
//创建窗口 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, this); //将this指针传给窗口过程 assert(hWnd); m_hWnd = hWnd; //将this指针保存在窗口类关联的额外空间中,在窗口过程中,可以GetWindowLong取回。 ::SetWindowLong(hWnd, GWL_USERDATA, (LONG)this); //标准控件窗口过程路由到WndProc m_lpfnOldWndProc = (WNDPROC)::GetWindowLong(hWnd, GWL_WNDPROC); if (m_lpfnOldWndProc != WndProc) { //子类化控件——用我们的回调函数代替操作系统提供的回调函数。 ::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)WndProc); //模拟发送WM_CREATE和WM_NCCREATE消息 //对于标准控件,因在CreateWindow时,窗体过程仍然是操作系统提供的,我们的WndProc会漏过这两个消息,所以模拟发送一下。 WindowProc(WM_CREATE, 0, 0); WindowProc(WM_NCCREATE, 0, 0); }
3.2.5 资源创建控件的子类化处理
(1)资源创建的控件——即控件是从资源中生成的,并没有调用我们封装的CreateEx函数,所以消息不能路由到我们提供的WndProc中,这里需要提供一个函数来处理这个问题。
BOOL QWnd::SubClassWindows(HWND hWnd) { if (m_hWnd == hWnd) return TRUE; m_lpfnOldWndProc = (WNDPROC)GetWindowLong(hWnd,GWL_WNDPROC); if(m_lpfnOldWndProc!=QWnd::WndProc) { m_hWnd =hWnd; SetWindowLong(m_hWnd,GWL_USERDATA,(LONG)this); SetWindowLong(m_hWnd,GWL_WNDPROC,(LONG)QWnd::WndProc); return TRUE; } return FALSE; }
【附完整源码】
/***************************main.c*****************************************************/ #include "QWinApp.h" #include "Global.h" int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { QWinApp* pWinApp = glbGetApp(); assert(pWinApp); pWinApp->m_hInstance = hInstance; pWinApp->InitInstance(); pWinApp->Run(); pWinApp->ExitInstance(); return TRUE; }
/****************************Global.h文件*******************************************/ #pragma once #include "stdAfx.h" #include "QWinApp.h" extern QWinApp* g_pWinApp; extern QWinApp* glbGetApp();// global Get App;
/****************************stdAfx.h文件*******************************************/ #pragma once #include <Windows.h> #include <tchar.h> #include <assert.h>
/****************************QWinApp.h文件*******************************************/ #pragma once #include "stdAfx.h" class QWinApp { public: QWinApp(); ~QWinApp(); public: virtual BOOL InitInstance(); virtual BOOL ExitInstance(); void Run(); public: LPVOID m_pMainWnd; HINSTANCE m_hInstance; };
/****************************QWinApp.c文件*******************************************/ #include "QWinApp.h" #include "Global.h" QWinApp::QWinApp() { m_pMainWnd = NULL; g_pWinApp = this; //将theApp指针保存在全局变量中g_pWinApp。 } QWinApp::~QWinApp() { } BOOL QWinApp::InitInstance() //接口,留给派生类去实现窗口注册、创建、显示等工作。 { return TRUE; } BOOL QWinApp::ExitInstance() //应用程序结束前的收尾工作,如销毁指针等。 { return TRUE; } void QWinApp::Run() //消息循环 { MSG msg; while (GetMessage(&msg,NULL,NULL,NULL)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
/****************************QMyWinApp.h文件*******************************************/ #pragma once #include "QWinApp.h" class QMyWinApp : public QWinApp { public: QMyWinApp(); ~QMyWinApp(); public: virtual BOOL InitInstance(); virtual BOOL ExitInstance(); }; extern QMyWinApp theApp;
/****************************QMyWinApp.c文件*******************************************/ #include "QMyWinApp.h" #include "QMainFrame.h" QMyWinApp::QMyWinApp() { } QMyWinApp::~QMyWinApp() { } QMyWinApp theApp; BOOL QMyWinApp::InitInstance() { QMainFrame* pMainFrame = new QMainFrame(); assert(pMainFrame); m_pMainWnd = pMainFrame; BOOL bRet = pMainFrame->CreateEx(0, _T("MyFirstClass"), _T("我的第一个窗口程序"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL); if (!bRet) { MessageBox(NULL, _T("创建窗口失败!"), _T("创建窗口"), 0); return FALSE; } pMainFrame->ShowWindow(SW_SHOW); pMainFrame->UpdateWindow(); return TRUE; } BOOL QMyWinApp::ExitInstance() { delete m_pMainWnd; m_pMainWnd = NULL; return TRUE; }
/****************************QWnd.h文件*******************************************/ #pragma once #include "stdAfx.h" class QWnd { public: QWnd(); ~QWnd(); public: HWND m_hWnd; WNDPROC m_lpfnOldWndProc; public: virtual BOOL PreCreateWindow(CREATESTRUCT& cs); //注册窗口类 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); BOOL ShowWindow(int nCmdShow); void UpdateWindow(); BOOL DestroyWindow(); public: static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); virtual LRESULT Default(UINT uMsg, WPARAM wParam, LPARAM lParam); public: virtual LRESULT OnCreate(WPARAM wParam, LPARAM lParam); virtual LRESULT OnClose(WPARAM wParam, LPARAM lParam); virtual LRESULT OnDestroy(WPARAM wParam, LPARAM lParam); virtual LRESULT OnPaint(WPARAM wParam, LPARAM lParam); public: BOOL SubClassWindow(HWND hWnd); static QWnd* FromHandle(HWND hWnd); };
/****************************QWnd.c文件*******************************************/ #include "QWnd.h" QWnd::QWnd() { m_hWnd = NULL; m_lpfnOldWndProc = NULL; } QWnd::~QWnd() { } BOOL QWnd::PreCreateWindow(CREATESTRUCT& cs) { WNDCLASSEX wc = {0}; //判断窗口类是否存在(比如标准控件(如按钮类),是由Window己经注册的,直接填充cs返回) //如果是标准控件,其窗口过程也是Windows而不是我们提供的。 BOOL bRet = GetClassInfoEx(cs.hInstance,cs.lpszClass,&wc); if (bRet) { return TRUE; } wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.cbSize = sizeof(wc); wc.hbrBackground = (HBRUSH)GetStockObject(COLOR_HIGHLIGHTTEXT); wc.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)); wc.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION)); wc.hIconSm = NULL; wc.hInstance = cs.hInstance; wc.lpfnWndProc = WndProc; //如果是标准控件,这里的代码将执行不到。窗口过程己经由操作系统提供了。 wc.lpszClassName = cs.lpszClass; wc.lpszMenuName = NULL; wc.style = CS_HREDRAW | CS_VREDRAW; return ::RegisterClassEx(&wc); } BOOL QWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hwndParent, HMENU nIDorHMenu, LPVOID lpParam) { //注册窗口 CREATESTRUCT cs; cs.cx = nWidth; cs.cy = nHeight; cs.dwExStyle = dwExStyle; cs.hInstance = (HINSTANCE)::GetModuleHandle(NULL); cs.hMenu = nIDorHMenu; cs.hwndParent = hwndParent; cs.lpCreateParams = lpParam; cs.lpszClass = lpszClassName; cs.lpszName = lpszWindowName; cs.style = dwStyle; cs.x = x; cs.y = y; BOOL bRet = PreCreateWindow(cs); if (!bRet) { return FALSE; } //创建窗口 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, this); //将this指针传给窗口过程 assert(hWnd); m_hWnd = hWnd; ::SetWindowLong(hWnd, GWL_USERDATA, (LONG)this); //将this指针保存在窗口类关联的额外空间中,在窗口过程中,可以GetWindowLong取回。 //标准控件窗口过程路由到WndProc m_lpfnOldWndProc = (WNDPROC)::GetWindowLong(hWnd, GWL_WNDPROC); if (m_lpfnOldWndProc != WndProc) { //子类化控件 ::SetWindowLong(hWnd, GWL_WNDPROC, (LONG)WndProc); //模拟发送WM_CREATE和WM_NCCREATE消息 WindowProc(WM_CREATE, 0, 0); //对于标准控件,因在CreateWindow时,窗体过程仍然是操作系统 WindowProc(WM_NCCREATE, 0, 0); //提供的,我们的WndProc会漏过这两个消息,所以模拟发送一下。 } return TRUE; } //供Windows调用的回调函数——全局函数或静态成员函数,但不能是普通的类成员函数 LRESULT CALLBACK QWnd::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { QWnd* pWnd = NULL; //处理CreateWindowEx过程中发送的两个消息中无法获取窗口对象指针的问题。 if (uMsg == WM_CREATE || uMsg == WM_NCCREATE) { CREATESTRUCT *pCs = (CREATESTRUCT*)lParam; if (pCs) { pWnd = (QWnd*)pCs->lpCreateParams; //窗口对象指针保存在lpCreateParams中 if (pWnd) { pWnd->m_hWnd = hWnd; //保存hWnd,因为WindowProc里面的函数可能用到m_hWnd //对于标准控件,m_lpfnOldWndProc记录的是操作系统提供的窗口过程 pWnd->m_lpfnOldWndProc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC); return pWnd->WindowProc(uMsg, wParam, lParam); } } } pWnd = (QWnd*)::GetWindowLong(hWnd, GWL_USERDATA); //获取窗口对象的指针 if (pWnd) { return pWnd->WindowProc(uMsg, wParam, lParam); //消息路由到窗体的WindowProc中 } return ::DefWindowProc(hWnd,uMsg,wParam,lParam); //非窗体对象,则按缺省处理 } BOOL QWnd::ShowWindow(int nCmdShow) { assert(m_hWnd); return ::ShowWindow(m_hWnd, nCmdShow); } void QWnd::UpdateWindow() { assert(m_hWnd); ::UpdateWindow(m_hWnd); } BOOL QWnd::DestroyWindow() { assert(m_hWnd); return ::DestroyWindow(m_hWnd); } LRESULT QWnd::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: return OnCreate(wParam, lParam); //虚函数OnCreate break; case WM_PAINT: return OnPaint(wParam, lParam); break; case WM_CLOSE: return OnClose(wParam, lParam); //虚函数OnClose break; case WM_DESTROY: return OnDestroy(wParam, lParam); //虚函数OnDestroy break; } return Default(uMsg, wParam, lParam); } LRESULT QWnd::Default(UINT uMsg, WPARAM wParam, LPARAM lParam) { //标准化控件的默认处理,由操作系统提供的窗体过程处理 if (m_lpfnOldWndProc != QWnd::WndProc) { return m_lpfnOldWndProc(m_hWnd, uMsg, wParam, lParam); } return ::DefWindowProc(m_hWnd, uMsg, wParam, lParam); } LRESULT QWnd::OnCreate(WPARAM wParam, LPARAM lParam) { return Default(WM_CREATE, wParam, lParam); } LRESULT QWnd::OnClose(WPARAM wParam, LPARAM lParam) { return Default(WM_CLOSE, wParam, lParam); } LRESULT QWnd::OnDestroy(WPARAM wParam, LPARAM lParam) { return Default(WM_DESTROY, wParam, lParam); } LRESULT QWnd::OnPaint(WPARAM wParam, LPARAM lParam) { return Default(WM_PAINT, wParam, lParam); } QWnd* QWnd::FromHandle(HWND hWnd) { assert(hWnd); return (QWnd*)GetWindowLong(hWnd, GWL_USERDATA); } BOOL QWnd::SubClassWindow(HWND hWnd) { assert(hWnd); //传进来的窗口与m_hWnd相同时,说明己经子类化过。 if (m_hWnd == hWnd) return TRUE; m_lpfnOldWndProc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC); //提取传进来窗口的窗口过程 if (m_lpfnOldWndProc != QWnd::WndProc) { //子类化 m_hWnd = hWnd; SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this); SetWindowLong(m_hWnd, GWL_WNDPROC,(LONG)QWnd::WndProc); return TRUE; } return FALSE; }
/****************************QFrameWnd.h文件*******************************************/ #pragma once #include "QWnd.h" #include "QButton.h" class QMainFrame : public QWnd { public: QMainFrame(); ~QMainFrame(); public: LRESULT OnClose(WPARAM wParam, LPARAM lParam); LRESULT OnDestroy(WPARAM wParam, LPARAM lParam); LRESULT OnCreate(WPARAM wParam, LPARAM lParam); public: QButton m_wndButton; };
/****************************QFrameWnd.c文件*******************************************/ #include "QMainFrame.h" #define IDC_BUTTON 100001 QMainFrame::QMainFrame() { } QMainFrame::~QMainFrame() { } //msg LRESULT QMainFrame::OnCreate(WPARAM wParam, LPARAM lParam) { if (NULL == m_wndButton.m_hWnd) { m_wndButton.CreateEx(_T("按我呀!"),WS_CHILD|WS_VISIBLE|DFCS_BUTTONPUSH,0,0,200,120,m_hWnd,(HMENU)IDC_BUTTON); } return TRUE; } LRESULT QMainFrame::OnClose(WPARAM wParam, LPARAM lParam) { if (IDYES == MessageBox(NULL, _T("确认要关闭窗口吗?"), _T("信息提示!"), MB_YESNO)) { return DestroyWindow(); } return TRUE; } LRESULT QMainFrame::OnDestroy(WPARAM wParam, LPARAM lParam) { ::PostQuitMessage(0); return TRUE; }
/****************************QButton.h文件*******************************************/ #pragma once #include "QWnd.h" class QButton :public QWnd { public: QButton(); ~QButton(); public: virtual BOOL CreateEx(LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hwndParent, HMENU nIDorHMenu); };
/****************************QButton.c文件*******************************************/ #include "QButton.h" QButton::QButton() { } QButton::~QButton() { } BOOL QButton::CreateEx(LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hwndParent, HMENU nIDorHMenu) { return QWnd::CreateEx(0, _T("BUTTON"), lpszWindowName, dwStyle, x, y, nWidth, nHeight, hwndParent, nIDorHMenu, NULL); }