闲时顺手翻了翻《Windows Graphics Programming》,发现里面的示例代码很不错,对设计应用程序框架和理解现有的应用程序框架很有帮助(尤其是MFC)。先来看一个很简单的用面向对象的思想包装API函数的KWindow类。示例代码贴出来,申明一下,代码不是我写的,但有可能做一些改动,加了很多罗嗦的注释,只是想节约以后阅读的时间。 下载示例project KWindow类需要完成注册窗口、创建窗口、处理窗口消息等用API编写程序时要处理的任务。由于每个窗口类对不同的消息会有不同的处理,所以首先想到了消息处理函数采用虚函数来实现(这种方式缺点是消息太多会使虚函数表过大,MFC采用消息映射,这里只是为了简单),因为虚函数肯定是成员函数,有this指针,Win32 API不能把一个虚函数作为窗口消息处理函数。通常的解决办法是采用静态函数,在这个静态函数里在想办法产生一个指向代表当前窗口的KWindow实例的指针来调用对应窗口的消息处理函数。
// win.h #pragma once // 以K开头来命名类,是希望与MFC有明显区别 class KWindow { protected: // 处理WM_PAINT消息,由WndProc调用 virtual void OnDraw(HDC hDC) { } // 处理WM_KEYDOWN消息,由WndProc调用 virtual void OnKeyDown(WPARAM wParam, LPARAM lParam) { } // 真正的消息分发/处理函数 virtual LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); // API中注册的消息处理函数,不能是成员函数,因为成员函数有this指针 static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); // 派生类可以在这里修改窗口的属性,如图标、菜单等 virtual void GetWndClassEx(WNDCLASSEX & wc); public: // 保存该窗口对应的HWND HWND m_hWnd; // m_hWnd 由CreateEx成员函数调用API函数CreateWindowEx赋值 KWindow(void) { m_hWnd = NULL; } // destructor virtual ~KWindow(void) { } // 调用API函数CreateWindowEx创建窗口 virtual bool CreateEx(DWORD dwExStyle, LPCTSTR lpszClass, LPCTSTR lpszName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hParent, HMENU hMenu, HINSTANCE hInst); // 注册窗口 bool RegisterClass(LPCTSTR lpszClass, HINSTANCE hInst); // 消息循环 virtual WPARAM MessageLoop(void);
BOOL ShowWindow(int nCmdShow) const { return ::ShowWindow(m_hWnd, nCmdShow); }
BOOL UpdateWindow(void) const { return ::UpdateWindow(m_hWnd); } };
// win.cpp #define STRICT #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <assert.h> #include <tchar.h> #include ".\win.h"
// 真正的消息分发/处理函数 LRESULT KWindow::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch( uMsg ) { case WM_KEYDOWN: OnKeyDown(wParam, lParam); return 0; case WM_PAINT: { PAINTSTRUCT ps;
BeginPaint(m_hWnd, &ps); OnDraw(ps.hdc); EndPaint(m_hWnd, &ps); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; }
return DefWindowProc(hWnd, uMsg, wParam, lParam); }
// API中注册的消息处理函数,将操作系统的消息分发到正确的KWindow对象 LRESULT CALLBACK KWindow::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { KWindow * pWindow;
if ( uMsg == WM_NCCREATE ) // 窗口创建时收到的第一个消息 { // 通过lParam找出该窗口对应的KWindow指针,并调用 // SetWindowLong(GWL_USERDATA)保存 assert( ! IsBadReadPtr((void *) lParam, sizeof(CREATESTRUCT)) ); MDICREATESTRUCT * pMDIC = (MDICREATESTRUCT *) ((LPCREATESTRUCT) lParam)->lpCreateParams; pWindow = (KWindow *) (pMDIC->lParam);
assert( ! IsBadReadPtr(pWindow, sizeof(KWindow)) ); SetWindowLong(hWnd, GWL_USERDATA, (LONG) pWindow); } else { // 调用GetWindowLong(GWL_USERDATA)找回在WM_NCCREATE消息中保存的 // KWindow指针 pWindow=(KWindow *)GetWindowLong(hWnd, GWL_USERDATA); }
if ( pWindow ) { return pWindow->WndProc(hWnd, uMsg, wParam, lParam); } else { return DefWindowProc(hWnd, uMsg, wParam, lParam); } }
bool KWindow::RegisterClass(LPCTSTR lpszClass, HINSTANCE hInst) { WNDCLASSEX wc;
if ( ! GetClassInfoEx(hInst, lpszClass, &wc) ) { GetWndClassEx(wc);
wc.hInstance = hInst; wc.lpszClassName = lpszClass; if ( !RegisterClassEx(&wc) ) return false; }
return true; }
bool KWindow::CreateEx(DWORD dwExStyle, LPCTSTR lpszClass, LPCTSTR lpszName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hParent, HMENU hMenu, HINSTANCE hInst) { if ( ! RegisterClass(lpszClass, hInst) ) return false;
// use MDICREATESTRUCT to pass this pointer, support MDI child window MDICREATESTRUCT mdic; memset(& mdic, 0, sizeof(mdic)); mdic.lParam = (LPARAM) this; m_hWnd = CreateWindowEx(dwExStyle, lpszClass, lpszName, dwStyle, x, y, nWidth, nHeight, hParent, hMenu, hInst, & mdic);
return m_hWnd != NULL; }
// 派生类中可以改写默认属性 void KWindow::GetWndClassEx(WNDCLASSEX & wc) { memset(& wc, 0, sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = NULL; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = NULL; wc.hIconSm = NULL; }
// Message Loop WPARAM KWindow::MessageLoop(void) { MSG msg;
while ( GetMessage(&msg, NULL, 0, 0) ) { TranslateMessage(&msg); DispatchMessage(&msg); }
return msg.wParam; }
// Hello.cpp #define STRICT #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <assert.h> #include <tchar.h>
#include "win.h"
const TCHAR szMessage[] = _T("Hello, World !"); const TCHAR szFace[] = _T("Times New Roman"); const TCHAR szHint[] = _T("Press ESC to quit."); const TCHAR szProgram[] = _T("HelloWorld3");
// copy CenterText from Hello2.cpp
class KHelloWindow : public KWindow { void CenterText(HDC hDC, int x, int y, LPCTSTR szFace, LPCTSTR szMessage, int point) { HFONT hFont = CreateFont( point * GetDeviceCaps(hDC, LOGPIXELSY) / 72, 0, 0, 0, FW_BOLD, TRUE, FALSE, FALSE, ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, PROOF_QUALITY, VARIABLE_PITCH, szFace); assert(hFont);
HGDIOBJ hOld = SelectObject(hDC, hFont);
SetTextAlign(hDC, TA_CENTER | TA_BASELINE);
SetBkMode(hDC, TRANSPARENT); SetTextColor(hDC, RGB(0, 0, 0xFF)); TextOut(hDC, x, y, szMessage, _tcslen(szMessage)); SelectObject(hDC, hOld); DeleteObject(hFont); }
void OnKeyDown(WPARAM wParam, LPARAM lParam) { if (wParam==VK_ESCAPE ) { PostMessage(m_hWnd, WM_CLOSE, 0, 0); } }
void OnDraw(HDC hDC) { TextOut(hDC, 0, 0, szHint, lstrlen(szHint)); CenterText(hDC, GetDeviceCaps(hDC, HORZRES)/2, GetDeviceCaps(hDC, VERTRES)/2, szFace, szMessage, 72); }
// 修改默认窗口属性 void GetWndClassEx(WNDCLASSEX & wc) { memset(& wc, 0, sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WindowProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = NULL; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); // 将背景画刷改为透明 wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = NULL; wc.hIconSm = NULL; }
public:
};
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR lpCmd, int nShow) { KHelloWindow win;
win.CreateEx(0, szProgram, szProgram, WS_POPUP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), NULL, NULL, hInst);
win.ShowWindow(nShow); win.UpdateWindow();
return win.MessageLoop(); }
|