閑時順手翻了翻《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();
}

|