MFC雖封裝了Win32 API,但其核心架構仍繼承自Windows SDK(可追溯至Windows 3.x時代)。理解SDK程式結構是掌握Win32開發、除錯MFC深層問題、甚至開發高效能系統級程式的關鍵。本文完整剖析標準SDK程式的 WinMain、視窗類別註冊、視窗建立、訊息迴圈與視窗程序四大支柱。
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
if (!hPrevInstance)
if (!InitApplication(hInstance))
return FALSE;
if (!InitInstance(hInstance, nCmdShow))
return FALSE;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
BOOL InitApplication(HINSTANCE hInstance)
{
WNDCLASS wc = {0};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(hInstance, "MyIcon");
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = "GenericMenu";
wc.lpszClassName = "MyWindowClass";
return RegisterClass(&wc);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd = CreateWindow("MyWindowClass", "Win32 SDK Demo",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
if (!hWnd) return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
| 參數 | 型別 | 含義 |
|---|---|---|
hInstance | HINSTANCE | 當前程式實例控制碼。同一程式多次執行 = 多實例,每實例唯一 |
hPrevInstance | HINSTANCE | Win32下永遠為 NULL(16位元Windows遺留) |
lpCmdLine | LPSTR | 命令列參數字串(不含程式名),相當C語言 argv |
nCmdShow | int | 視窗初始顯示狀態(SW_SHOW、SW_MINIMIZE等) |
實例 = 程式邏輯在記憶體中的一次具體執行實體。三顆蘋果 = 「蘋果」類別的三個實例。控制碼 = 32位整數,代表核心物件的間接參考。
「視窗類別」是視窗的模板/藍圖,非C++類別。RegisterClass(&WNDCLASS) 向系統登錄:
| WNDCLASS欄位 | 用途 | 範例值 |
|---|---|---|
style | 類別樣式 | CS_HREDRAW | CS_VREDRAW(尺寸變化重繪) |
lpfnWndProc | 視窗程序指標 | WndProc(必須符合標準原型) |
hInstance | 擁有者實例 | hInstance |
hIcon | 類別圖示 | LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON)) |
hCursor | 類別游標 | LoadCursor(NULL, IDC_ARROW) |
hbrBackground | 背景筆刷 | GetStockObject(WHITE_BRUSH) |
lpszClassName | 類別名稱 | "MyWindowClass"(CreateWindow引用) |
HWND CreateWindow(
LPCTSTR lpClassName, // 註冊時的lpszClassName
LPCTSTR lpWindowName, // 標題列文字
DWORD dwStyle, // WS_OVERLAPPEDWINDOW等
int x, int y, // 位置
int nWidth, int nHeight, // 大小
HWND hWndParent, // 父視窗
HMENU hMenu, // 選單/控制項ID
HINSTANCE hInstance, // 實例控制碼
LPVOID lpParam // 建立參數傳給WM_CREATE
);
HWND(視窗控制碼),失敗回傳 NULLShowWindow() + UpdateWindow() 強制重繪
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
MSG 結構WM_QUIT 回傳 FALSE(0),結束迴圈TRUE(非零)WM_KEYDOWN + WM_KEYUP → WM_CHAR / WM_DEADCHARWM_SYSKEYDOWN + WM_SYSKEYUP → WM_SYSCHAR / WM_SYSDEADCHARMSG.hwnd 取得目標視窗控制碼lpfnWndProcWndProc(hwnd, msg, wParam, lParam)
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_CREATE: // 視窗建立時
// 初始化資源、建立子控制項
break;
case WM_PAINT: // 需重繪
{ PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); /* 繪圖 */ EndPaint(hWnd, &ps); }
break;
case WM_COMMAND: // 選單/控制項通知
switch (LOWORD(wParam)) { case IDM_EXIT: DestroyWindow(hWnd); break; }
break;
case WM_DESTROY: // 視窗銷毀
PostQuitMessage(0); // 發送WM_QUIT給訊息佇列
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam); // 預設處理
}
return 0;
}
wParam、lParam 依訊息類型語意不同(查閱 WinUser.h 或 MSDN)LOWORD(lParam)、HIWORD(lParam)、GET_X_LPARAM(lParam)、GET_Y_LPARAM(lParam)WM_USER + n(視窗私有)、RegisterWindowMessage(系統全域唯一)| SDK概念 | MFC對應 | 說明 |
|---|---|---|
WinMain | CWinApp::InitInstance/Run | MFC框架內建WinMain |
WNDCLASS | CWnd::Create/PreCreateWindow | MFC封裝註冊細節 |
HWND | CWnd::m_hWnd | Handle封裝為protected成員 |
WndProc | 訊息映射 + 虛函數 | 巨集映射替代switch-case |
GetMessage/Dispatch | CWinThread::Run | 框架層處理訊息泵 |
W/A 後綴或用 TCHAR 宏Create 配對 Destroy/Delete(DC、筆刷、字型、位圖...)WM_ERASEBKGND 回傳非零、雙緩衝繪圖dpiAware,使用 GetDpiForWindow 縮放Win32 SDK以 實例控制碼 → 視窗類別註冊 → 視窗建立 → 訊息迴圈 → 視窗程序 五步構成完整程式骨架。MFC在此基礎上以C++類別封裝Handle、以訊息映射巨集替代Switch分派、以文檔/視圖架構組織應用邏輯。深入理解SDK層機制,才能在MFC無法覆蓋場景下靈活介入底層、解決疑難雜症、編寫高效能系統程式。