快捷搜索:  汽车  科技

vc怎么生成窗口:VC 将win32封装成类库 理解封装思想和提高代码通用性思路

vc怎么生成窗口:VC 将win32封装成类库 理解封装思想和提高代码通用性思路也就是将不常用的代码移到.h文件中,将常用的代码移到.cpp文件中去。// 将win32程序封装成类库1 // 工程→设置→连接→工程选项:将 /subsystem:console改成windows // Win32Lib.cpp文件 #include <windows.h> #include <stdio.h> #include "miniMFC.h" CMyApp theApp; //入口函数 int WINAPI WinMain( HINSTANCE hInstance // handle to current instance HINSTANCE hPrevInstance // handle to previous instance LPSTR lpCmdLine // co

类是对数据与函数的封装,所以将一些重复性高的结构代码封装成类,然后通过继承和改写可以进一步提高代码的重用性。

前文的工程都还是全局函数的形式,现在我们来将Win32Lib封装成class。

将前文的Win32Lib工程中复制一份,将目录改名为miniMFC,我们将在此基础上封装我们的类。
Win32Lib工程中,我们仅仅是改造了WinProc()这个窗口处理函数,对WinMain()主函数并没有改造。仔细分析WinMain()函数,发现其本质就做了2件事情:一是创建和显示窗口,二是进行消息循环以响应窗口消息。

我们可以封装一个CMyWnd类,专门负责和窗口创建相关的工作,而在入口函数WinMain()中实例化CMyWnd,并调用其成员函数。

进一步的,可以封装一个CMyApp类,在CMyApp类中定义成员函数InitInstance(),将WinMain()中的代码移到InitInstance()中来,专门负责初始化,定义成员函数Run(),来负责程序的运行(消息循环)等应用性的工作。

Win32Lib.h

// 将win32程序封装成类库1 // 工程→设置→连接→工程选项:将 /subsystem:console改成windows // Win32Lib.cpp文件 #include <windows.h> #include <stdio.h> #include "miniMFC.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::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 10 50 (LPTSTR)"win32封装成类库" strlen("win32封装成类库")); 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)); TextOut(hdc 10 50 (LPTSTR)"请按下左键试一试!" strlen("请按下左键试一试!")); 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::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(); }

Win32Lib.cpp

// 将win32程序封装成类库1 // 工程→设置→连接→工程选项:将 /subsystem:console改成windows // Win32Lib.cpp文件 #include <windows.h> #include <stdio.h> #include "miniMFC.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::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 10 50 (LPTSTR)"win32封装成类库" strlen("win32封装成类库")); 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)); TextOut(hdc 10 50 (LPTSTR)"请按下左键试一试!" strlen("请按下左键试一试!")); 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::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(); }

以上代码虽有实现将函数封装到类,但需要做为interface的.h文件保持稳定性,库使用者尽量只在.cpp文件中写业务逻辑(主要是消息响应函数)。

也就是将不常用的代码移到.h文件中,将常用的代码移到.cpp文件中去。

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::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::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

// 将win32程序封装成类库2 // 工程→设置→连接→工程选项:将 /subsystem:console改成windows #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 "miniMFC.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 10 50 (LPTSTR)"封装win32为类库" strlen("封装win32为类库")); 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)); TextOut(hdc 10 50 (LPTSTR)"请点击左键试试!" strlen("请点击左键试试!")); 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; }

应当说,Win32Lib.h是可以进一步将其中的实现函数放到一个新的.cpp文件中去的,这样真正实现interface与implementation的分享。

上面代码其中使用MY_MESSAGE_DECLARE和MY_MESSAGE_MAP这2个宏将原本在Win32Class.h文件中的声明消息响应函数和定义消息响应函数数组的代码也移到了Win32MFC.cpp文件中。这样,.h文件中都是一些结构性和固定模式的不需要经常改动的内容了,而.cpp文件中的内容就全是需要经常改动、与应用相关的内容,如要再添加新的消息及其响应代码,只需要照葫芦画瓢即可,真正实现了将主要精力放在业务上而不是程序的框架上的目标。

我们知道,MFC的复杂之处在于,既有C又有C ,既有win32API,又有MFC对win32API的封装,再加上没有通过虚函数的多态(主要是考虑到使用虚函数会造成过大的系统开销),而是通过宏进行的消息映射,所以MFC复杂晦涩。其实,上面2个宏的实现思想就是MFC中消息映射的核心思想,通过实现我们自己的类库的过程,我们对于代码封装即库界面与实现的分工便会有更深刻的理解了。

我们看C:\Program Files\Microsoft Visual Studio\VC98\MFC\Include\AFXWIN.H内的源代码:

1 DECLARE_MESSAGE_MAP()宏

#define DECLARE_MESSAGE_MAP() \ private: \ static const AFX_MSGMAP_ENTRY _messageEntries[]; \ protected: \ static AFX_DATA const AFX_MSGMAP messageMap; \ static const AFX_MSGMAP* PASCAL _GetBaseMessageMap(); \ virtual const AFX_MSGMAP* GetMessageMap() const; \

_messageEntries[]的类型是结构体AFX_MSGMAP_ENTRY:

struct AFX_MSGMAP_ENTRY // { UINT nMessage; // windows message UINT nCode; // control code or WM_NOTIFY code UINT nID; // control ID (or 0 for windows messages) UINT nLastID; // used for entries specifying a range of control id's UINT nSig; // signature type (action) or pointer to message # AFX_PMSG pfn; // routine to call (or special value) };

相对于上面自定义消息映射结构体只定义了2个成员,这里有6个成员。

2 BEGIN_MESSAGE_MAP和END_MESSAGE_MAP定义

作用是向类中添加消息映射必要的结构体和函数声明

BEGIN_MESSAGE_MAP(theClass baseClass) //{{AFX_MSG_MAP(theClass) ON_BN_CLICKED(id memberFxn) //}}AFX_MSG_MAP END_MESSAGE_MAP()

对应的宏:

END_MESSAGE_MAP和BEGIN_MESSAGE_MAP是成对出现的 首先看定义 #define BEGIN_MESSAGE_MAP(theClass baseClass) / const AFX_MSGMAP* theClass::GetMessageMap() const / { return &theClass::messageMap; } / AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = / { &baseClass::messageMap &theClass::_messageEntries[0] }; / AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = / { / #define END_MESSAGE_MAP() / {0 0 0 0 AfxSig_end (AFX_PMSG)0 } / }; /

DECLARE_MESSAGE_MAP、BEGIN_MESSAGE_MAP、END_MESSAGE_MAP以及ON_COMMAND、ON_MESSAGE等宏最终作用的结果是在你的类中生成了一个名为_messageEntries[]的数组,该数组中将填入每个你关心的消息和此消息对应的处理函数。应用程序框架生成的WindowProc接收到一个消息后,会按照一定的原则轮询个各类(CView、CDocument、CFrameWnd、CWinApp)的_messageEntries[]数组,检查该数组中有没有对应的消息,如果有,就调用相应的响应函数;如果没有,就换下一个类继续检查。当所有的有关的类都被检查完后仍未发现响应函数时,便将此消息丢给DefWindowProc处理。

MFC把窗口函数一致设为AfxWndProc()。

MFC2.5 的CWinApp::Run 调用PumpMessage,后者又调用::DispatchMessage,把消息源源推往AfxWndProc,最后流向pWnd->WindowProc 去。

事实上,MFC 4.x 利用hook,把看似无关的动作全牵联起来了。所谓hook,是Windows程序设计中的一种高阶技术。通常消息都是停留在消息队列中等待被所隶属之窗口抓取,如果你设立hook,就可以更早一步抓取消息,并且可以抓取不属于你的消息,送往你设定的一个所谓「滤网函数(filter)」。

vc怎么生成窗口:VC 将win32封装成类库 理解封装思想和提高代码通用性思路(1)

ref

https://blog.csdn.net/shimazhuge/article/details/8279936

https://www.bilibili.com/video/BV1LK4y1v7o1?p=2

-End-

猜您喜欢: