前面两个工程都还是全局函数的形式,现在我们来将Win32Lib封装成class,为向MFC过渡埋下伏笔。
Win32Lib工程中,我们仅仅是改造了WinProc()这个窗口处理函数,对WinMain()主函数并没有改造。仔细分析WinMain()函数,发现其本质就做了2件事情:一是创建和显示窗口,一是进行消息循环以响应窗口消息。因此,我们的封装思想也是基于这2点的。将其封装成2个类:CMyWnd类,和CMyApp类。CMyWnd类专门负责和窗口创建相关的工作,CMyApp类专门负责初始化、程序的运行(消息循环)等应用性的工作。
将Win32Lib工程复制一份,将目录改名为Win32Class,我们将在此基础上封装我们的Win32Class类。打开Win32Class工程,添加一个新类CMyWnd。
函数WinMain()中的代码在编写任何一个Win32工程时几乎都是上述固定的步骤,即注册窗口类,创建窗口,显示窗口,更新窗口,消息循环。
Win32Lib.h文件
// Win32Lib.h: interface for the CMyWnd class. ////////////////////////////////////////////////////////////////////// #ifndef AFX_MYWIN32LIB_H_ #define AFX_MYWIN32LIB_H_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 //返回元素的个数 #define dim(x)(sizeof(x) / sizeof(x[0])) //定义函数指针 typedef LRESULT(*FXN)(HWND, UINT, WPARAM, LPARAM); //消息映射结构 struct tagMESSAGEMAP { UINT Code; //消息 FXN Fxn; //响应函数 }; class CMyWnd { public: HINSTANCE m_hInstance; HWND m_hWnd; public: BOOL UpdateWindow(); BOOL ShowWindow(); BOOL Create(); CMyWnd(); virtual ~CMyWnd(){}; //主窗口回调函数 static LRESULT CALLBACK WinProc(HWND, UINT, WPARAM, LPARAM); //声明消息响应函数 static LRESULT OnChar(HWND, UINT, WPARAM, LPARAM); static LRESULT OnLButtonDown(HWND, UINT, WPARAM, LPARAM); static LRESULT OnPaint(HWND, UINT, WPARAM, LPARAM); static LRESULT OnDestroy(HWND, UINT, WPARAM, LPARAM); static LRESULT OnTimer(HWND, UINT, WPARAM, LPARAM); }; //消息映射数组 tagMESSAGEMAP MessageMaps[] = { WM_CHAR, CMyWnd::OnChar, WM_LBUTTONDOWN, CMyWnd::OnLButtonDown, WM_PAINT, CMyWnd::OnPaint, WM_DESTROY, CMyWnd::OnDestroy, WM_TIMER, CMyWnd::OnTimer, }; class CMyApp { public: CMyWnd* m_pMainWnd; //主窗口 public: CMyApp(); virtual ~CMyApp(){}; BOOL InitInstance(); //初始化主窗口 BOOL Run(); //消息循环 }; #endif // defined(AFX_MYWIN32LIB_H_)
Win32Lib.cpp文件
#include <windows.h> #include <stdio.h> #include "Win32Lib.h" CMyApp theApp; //入口函数 int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow) // show state { return 0; } ////////////////////////////////////////////////////////////////////// // CMyWnd Class ////////////////////////////////////////////////////////////////////// CMyWnd::CMyWnd() { m_hWnd = NULL; m_hInstance = NULL; } BOOL CMyWnd::Create() { WNDCLASS wndcls; wndcls.cbClsExtra = 0; wndcls.cbWndExtra = 0; wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndcls.hCursor = LoadCursor(NULL, IDC_ARROW); wndcls.hIcon = LoadIcon(NULL, IDI_ERROR); wndcls.hInstance = m_hInstance; wndcls.lpfnWndProc = WinProc; wndcls.lpszClassName = (LPTSTR)"ItJob2010"; wndcls.lpszMenuName = NULL; wndcls.style = CS_HREDRAW | CS_VREDRAW; RegisterClass(&wndcls); m_hWnd = ::CreateWindow(wndcls.lpszClassName, (LPTSTR)"培训中心", WS_OVERLAPPEDWINDOW, 0, 0, 600, 400, NULL, NULL, m_hInstance, NULL); ::SetTimer(m_hWnd, 123, 1000, NULL); if (m_hWnd == NULL) return FALSE; else return TRUE; } BOOL CMyWnd::ShowWindow() { return ::ShowWindow(m_hWnd, SW_SHOWNORMAL); } BOOL CMyWnd::UpdateWindow() { return ::UpdateWindow(m_hWnd); } //主窗口回调函数 LRESULT CALLBACK CMyWnd::WinProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { // 如果当前消息是我们关心的、定义在数组中的消息,则处理之 for (int i = 0; i < dim(MessageMaps); i++) { if (wMsg == MessageMaps[i].Code) { FXN iFxn = MessageMaps[i].Fxn; LRESULT lResult = iFxn(hWnd, wMsg, wParam, lParam); if (lResult == 0) return 0; } } // 否则,将消息交给系统去处理 return DefWindowProc(hWnd, wMsg, wParam, lParam); } //消息响应函数: 字符按下 LRESULT CMyWnd::OnChar(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { char szChar[20]; sprintf(szChar, "char is %c", (char)wParam); MessageBox(hWnd, (LPTSTR)szChar, (LPTSTR)"OnChar", 0); return 0; } //消息响应函数: 鼠标左键按下 LRESULT CMyWnd::OnLButtonDown(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { HDC hdc; hdc = GetDC(hWnd); TextOut(hdc, 0, 50, (LPTSTR)"计算机编程语言培训", strlen("计算机编程语言培训")); ReleaseDC(hWnd, hdc); return 0; } //消息响应函数:重绘窗口 LRESULT CMyWnd::OnPaint(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { //画一个圆 RECT rc; GetClientRect(hWnd, &rc); int iR = min(rc.right - rc.left, rc.bottom - rc.top) / 2; iR = iR * 4 / 5; POINT pt; pt.x = (rc.right + rc.left) / 2; pt.y = (rc.bottom + rc.top) / 2; HDC hdc; PAINTSTRUCT ps; hdc = BeginPaint(hWnd, &ps); ::Ellipse(hdc, pt.x - iR, pt.y - iR, pt.x + iR, pt.y + iR); MoveToEx(hdc, pt.x, pt.y,(LPPOINT)NULL); LineTo(hdc, pt.x + iR, pt.y); //显示时间 static char stime[] = "23:59:59"; SYSTEMTIME tm; ::GetLocalTime(&tm); sprintf(stime, "%.2d:%.2d:%.2d", tm.wHour, tm.wMinute, tm.wSecond); ::TextOut(hdc, 10, 10,(LPTSTR)stime, strlen(stime)); EndPaint(hWnd, &ps); return 0; } //消息响应函数: 销毁窗口 LRESULT CMyWnd::OnDestroy(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { PostQuitMessage(0); return 0; } //消息响应函数: 定时器 LRESULT CMyWnd::OnTimer(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { RECT rc; ::GetClientRect(hWnd, &rc); ::InvalidateRect(hWnd, &rc, TRUE); return 0; } ////////////////////////////////////////////////////////////////////// // CMyApp Class ////////////////////////////////////////////////////////////////////// CMyApp::CMyApp() { m_pMainWnd = NULL; if (InitInstance()) Run(); } BOOL CMyApp::Run() { MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return TRUE; } BOOL CMyApp::InitInstance() { m_pMainWnd = new CMyWnd(); m_pMainWnd->Create(); m_pMainWnd->ShowWindow(); return m_pMainWnd->UpdateWindow(); }
优化Win32Class,将不常用的代码移到.h文件中,将常用的代码移到.cpp文件中去。
其中使用MY_MESSAGE_DECLARE和MY_MESSAGE_MAP这2个宏将原本在Win32Class.h文件中的声明消息响应函数和定义消息响应函数数组的代码也移到了Win32Class.cpp文件中。这样,.h文件中都是一些结构性和固定模式的不需要经常改动的内容了,而.cpp文件中的内容就全是需要经常改动、和应用相关的内容,如要再添加新的消息及其响应代码,只需要照葫芦画瓢即可,真正实现了将主要精力放在业务上而不是程序的框架上的目标。
其实,上面2个宏的实现思想就是MFC中消息映射的核心思想,通过实现我们自己的类库的过程,将原本复杂晦涩、难于理解的MFC消息映射的概念轻松的揭示给了大家。这为我们后面学习MFC打下了良好的基础。
Win32Lib.h文件
// Win32Lib.h: interface for the CMyWnd class. ////////////////////////////////////////////////////////////////////// #ifndef AFX_MYWIN32LIB_H_ #define AFX_MYWIN32LIB_H_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 //返回元素的个数 #define dim(x)(sizeof(x) / sizeof(x[0])) //定义函数指针 typedef LRESULT(*FXN)(HWND, UINT, WPARAM, LPARAM); //消息映射结构 struct tagMESSAGEMAP { UINT Code; //消息 FXN Fxn; //响应函数 }; class CMyWnd { public: HINSTANCE m_hInstance; HWND m_hWnd; public: BOOL Create(); BOOL ShowWindow(); BOOL UpdateWindow(); CMyWnd(); virtual ~CMyWnd(){}; //主窗口回调函数 static LRESULT CALLBACK WinProc(HWND, UINT, WPARAM, LPARAM); //声明消息响应函数 MY_MESSAGE_DECLARE }; //消息映射数组 MY_MESSAGE_MAP class CMyApp { public: CMyWnd* m_pMainWnd; BOOL InitInstance(); BOOL Run(); CMyApp(); virtual ~CMyApp(){}; }; //入口函数 int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow) // show state { return 0; } ////////////////////////////////////////////////////////////////////// // CMyWnd Class ////////////////////////////////////////////////////////////////////// CMyWnd::CMyWnd() { m_hWnd = NULL; m_hInstance = NULL; } BOOL CMyWnd::Create() { WNDCLASS wndcls; wndcls.cbClsExtra = 0; wndcls.cbWndExtra = 0; wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wndcls.hCursor = LoadCursor(NULL, IDC_ARROW); wndcls.hIcon = LoadIcon(NULL, IDI_ERROR); wndcls.hInstance = m_hInstance; wndcls.lpfnWndProc = WinProc; wndcls.lpszClassName = (LPTSTR)"ItJob2010"; wndcls.lpszMenuName = NULL; wndcls.style = CS_HREDRAW | CS_VREDRAW; RegisterClass(&wndcls); m_hWnd = ::CreateWindow(wndcls.lpszClassName, (LPTSTR)"培训中心", WS_OVERLAPPEDWINDOW, 0, 0, 600, 400, NULL, NULL, m_hInstance, NULL); ::SetTimer(m_hWnd, 123, 1000, NULL); if (m_hWnd == NULL) return FALSE; else return TRUE; } BOOL CMyWnd::ShowWindow() { return ::ShowWindow(m_hWnd, SW_SHOWNORMAL); } BOOL CMyWnd::UpdateWindow() { return ::UpdateWindow(m_hWnd); } //主窗口回调函数 LRESULT CALLBACK CMyWnd::WinProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { // 如果当前消息是我们关心的、定义在数组中的消息,则处理之 for (int i = 0; i < dim(MessageMaps); i++) { if (wMsg == MessageMaps[i].Code) { FXN iFxn = MessageMaps[i].Fxn; LRESULT lResult = iFxn(hWnd, wMsg, wParam, lParam); if (lResult == 0) return 0; } } // 否则,将消息交给系统去处理 return DefWindowProc(hWnd, wMsg, wParam, lParam); } ////////////////////////////////////////////////////////////////////// // CMyApp Class ////////////////////////////////////////////////////////////////////// CMyApp::CMyApp() { m_pMainWnd = NULL; if (InitInstance()) Run(); } BOOL CMyApp::Run() { MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return TRUE; } BOOL CMyApp::InitInstance() { m_pMainWnd = new CMyWnd(); m_pMainWnd->Create(); m_pMainWnd->ShowWindow(); return m_pMainWnd->UpdateWindow(); } #endif // defined(AFX_MYWIN32LIB_H_)
Win32Lib.cpp文件
#include <windows.h> #include <stdio.h> //声明消息响应函数 #define MY_MESSAGE_DECLARE \ static LRESULT OnChar(HWND, UINT, WPARAM, LPARAM); \ static LRESULT OnLButtonDown(HWND, UINT, WPARAM, LPARAM); \ static LRESULT OnPaint(HWND, UINT, WPARAM, LPARAM); \ static LRESULT OnDestroy(HWND, UINT, WPARAM, LPARAM); \ static LRESULT OnTimer(HWND, UINT, WPARAM, LPARAM); \ //消息映射数组 #define MY_MESSAGE_MAP \ tagMESSAGEMAP MessageMaps[] = { \ WM_CHAR, CMyWnd::OnChar, \ WM_LBUTTONDOWN, CMyWnd::OnLButtonDown, \ WM_PAINT, CMyWnd::OnPaint, \ WM_DESTROY, CMyWnd::OnDestroy, \ WM_TIMER, CMyWnd::OnTimer, \ }; #include "Win32Lib.h" CMyApp theApp; //消息响应函数: 字符按下 LRESULT CMyWnd::OnChar(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { char szChar[20]; sprintf(szChar, "char is %c", (char)wParam); MessageBox(hWnd, (LPTSTR)szChar, (LPTSTR)"OnChar", 0); return 0; } //消息响应函数: 鼠标左键按下 LRESULT CMyWnd::OnLButtonDown(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { HDC hdc; hdc = GetDC(hWnd); TextOut(hdc, 0, 50, (LPTSTR)"计算机编程语言培训", strlen("计算机编程语言培训")); ReleaseDC(hWnd, hdc); return 0; } //消息响应函数:重绘窗口 LRESULT CMyWnd::OnPaint(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { //画一个圆 RECT rc; GetClientRect(hWnd, &rc); int iR = min(rc.right - rc.left, rc.bottom - rc.top) / 2; iR = iR * 4 / 5; POINT pt; pt.x = (rc.right + rc.left) / 2; pt.y = (rc.bottom + rc.top) / 2; HDC hdc; PAINTSTRUCT ps; hdc = BeginPaint(hWnd, &ps); ::Ellipse(hdc, pt.x - iR, pt.y - iR, pt.x + iR, pt.y + iR); MoveToEx(hdc, pt.x, pt.y,(LPPOINT)NULL); LineTo(hdc, pt.x + iR, pt.y); //显示时间 static char stime[] = "23:59:59"; SYSTEMTIME tm; ::GetLocalTime(&tm); sprintf(stime, "%.2d:%.2d:%.2d", tm.wHour, tm.wMinute, tm.wSecond); ::TextOut(hdc, 10, 10,(LPTSTR)stime, strlen(stime)); EndPaint(hWnd, &ps); return 0; } //消息响应函数: 销毁窗口 LRESULT CMyWnd::OnDestroy(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { PostQuitMessage(0); return 0; } //消息响应函数: 定时器 LRESULT CMyWnd::OnTimer(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) { RECT rc; ::GetClientRect(hWnd, &rc); ::InvalidateRect(hWnd, &rc, TRUE); return 0; }