在 Windows 作業系統中,所有可見的視窗(按鈕、編輯框、主視窗等)在建立之前,系統必須知道如何繪製它、如何處理使用者輸入。這份「藍圖」就是視窗類別。注意:這裡的「類別」不是 C++ 的 class,而是一個 WNDCLASS/WNDCLASSEX 結構體,包含視窗風格、訊息處理函數、圖示、游標、背景筆刷、選單、類別名稱等資訊。
ANSI 版本定義如下:
typedef struct tagWNDCLASSA {
UINT style; // 類別風格(CS_HREDRAW | CS_VREDRAW 等)
WNDPROC lpfnWndProc; // 訊息處理函數指標
int cbClsExtra; // 類別額外記憶體位元組
int cbWndExtra; // 視窗實例額外記憶體位元組
HINSTANCE hInstance; // 應用程式實例控制代碼
HICON hIcon; // 視窗圖示
HCURSOR hCursor; // 游標樣式
HBRUSH hbrBackground; // 背景筆刷
LPCSTR lpszMenuName; // 選單資源名稱
LPCSTR lpszClassName; // 類別名稱(唯一識別字串)
} WNDCLASSA, *PWNDCLASSA;
填妥結構後,呼叫 RegisterClass 或 RegisterClassEx 向系統登記,後續即可用類別名稱呼叫 CreateWindow/CreateWindowEx 快速建立視窗,實現「一次註冊,多次建立」的複用機制。
經典的 WinMain 入口點程式碼示範:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("HelloWin");
WNDCLASS wndclass = {0};
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszClassName = szAppName;
RegisterClass(&wndclass);
HWND hwnd = CreateWindow(szAppName, TEXT("Hello"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
流程清晰:填寫結構 → 註冊 → 建立視窗 → 訊息迴圈。一切由程式設計師手動控制。
使用 MFC 精靈建立專案時,我們看不到上述代碼,因為 MFC 在框架層已完成封裝。MFC 內建五個預設視窗類別(定義於 AFXIMPL.h 與 WINCORE.cpp):
| 巨集常數 | 對應字串名稱 | 用途 |
|---|---|---|
| AFX_WND_REG | "WND" | 一般視窗 |
| AFX_WNDCONTROLBAR_REG | "ControlBar" | 控制列 |
| AFX_WNDMDIFRAME_REG | "MDIFrame" | MDI 主框架 |
| AFX_WNDFRAMEORVIEW_REG | "FrameOrView" | 框架/視圖 |
| AFX_WNDOLECONTROL_REG | "OleControl" | OLE 控制項 |
這些字串最終由全域函式 AfxDeferRegisterClass 延遲註冊,核心邏輯在 AfxEndDeferRegisterClass 中呼叫 AfxRegisterClass → RegisterClass。下集將深入追蹤 MFC 如何在 CWnd::CreateEx → PreCreateWindow 鏈結中自動選擇正確類別。