VCL TForm 封裝了所有細節,但理解底層 RegisterClassEx、CreateWindowEx、GetMessage/DispatchMessage 消息循環,是掌握 Windows 程序本質、調試疑難雜癥、編寫高性能/無 VCL 依賴工具 (如引導程序、系統服務 UI) 的前提。
program DelphiAPIWindow;
uses Windows, Messages;
const
HelloStr = 'Hello World!';
{$R *.res} // 包含圖標資源 MAINICON
// 窗口過程:處理所有發送給該類窗口的消息
function MyWinProc(hWnd: HWND; uMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
var
hdc: HDC; ps: TPaintStruct; rect: TRect; hFont: HFONT;
begin
Result := 0;
case uMsg of
WM_PAINT:
begin
hdc := BeginPaint(hWnd, ps);
SetBkMode(hdc, TRANSPARENT);
GetClientRect(hWnd, rect);
// 繪製居中文本
DrawText(hdc, PChar(HelloStr), -1, rect, DT_SINGLELINE or DT_CENTER or DT_VCENTER);
EndPaint(hWnd, ps);
end;
WM_CREATE:
begin
// 創建自定義字體 (可選)
hdc := GetDC(hWnd);
hFont := CreateFont(45, 0, 0, 0, FW_NORMAL, 0, 0, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, VARIABLE_PITCH or FF_SWISS, 'Arial');
SelectObject(hdc, hFont);
ReleaseDC(hWnd, hdc);
// 注意:此處創建的字體未保存句柄,WM_DESTROY 會泄漏。實際應存入窗口數據 (SetWindowLongPtr GWLP_USERDATA) 或類數據。
end;
WM_DESTROY:
PostQuitMessage(0); // 發送 WM_QUIT 結束消息循環
else
Result := DefWindowProc(hWnd, uMsg, wParam, lParam); // 默認處理
end;
end;
// 程序入口
var
Msg: TMsg; hWnd, hInst: HINST; WndClass: TWndClassEx;
begin
hInst := GetModuleHandle(nil); // 獲取模塊實例句柄
// 1. 填充窗口類結構
WndClass.cbSize := SizeOf(TWndClassEx);
WndClass.lpszClassName := 'MyAPIWindowClass';
WndClass.style := CS_HREDRAW or CS_VREDRAW or CS_OWNDC;
WndClass.hInstance := hInst;
WndClass.lpfnWndProc := @MyWinProc; // 關鍵:指定窗口過程
WndClass.cbClsExtra := 0; WndClass.cbWndExtra := 0;
WndClass.hIcon := LoadIcon(hInst, MakeIntResource('MAINICON')); // 從資源加載圖標
WndClass.hIconSm := LoadIcon(hInst, MakeIntResource('MAINICON'));
WndClass.hCursor := LoadCursor(0, IDC_ARROW);
WndClass.hbrBackground := GetStockObject(WHITE_BRUSH); // 白色背景刷
WndClass.lpszMenuName := nil;
// 2. 註冊窗口類
if RegisterClassEx(WndClass) = 0 then
begin MessageBox(0, '窗口類註冊失敗!', 'Error', MB_OK); Exit; end;
// 3. 創建窗口實例
hWnd := CreateWindowEx(
WS_EX_OVERLAPPEDWINDOW, // 擴展風格
WndClass.lpszClassName, // 類名
'Hello API Window', // 窗口標題
WS_OVERLAPPEDWINDOW, // 風格
CW_USEDEFAULT, 0, // 位置 (x, y)
CW_USEDEFAULT, 0, // 尺寸 (寬, 高)
0, 0, // 父窗口、菜單
hInst, // 實例句柄
nil // 創建參數
);
if hWnd = 0 then
begin MessageBox(0, '窗口創建失敗!', 'Error', MB_OK); Exit; end;
// 4. 顯示與更新
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE or SWP_NOSIZE); // 置頂演示
// 5. 消息循環:核心引擎
while GetMessage(Msg, 0, 0, 0) do
begin
TranslateMessage(Msg); // 鍵盤虛擬鍵 -> 字符消息 (WM_CHAR)
DispatchMessage(Msg); // 分發到 MyWinProc
end;
end.
| 步驟 | API/結構 | 核心作用 |
|---|---|---|
| 類定義 | TWndClassEx | 定義窗口「模板」:過程指釖、圖標、光標、背景刷、類名。 |
| 類註冊 | RegisterClassEx | 向系統登記類信息,返回原子標識。同一進程同類名只需註冊一次。 |
| 窗口創建 | CreateWindowEx | 實例化窗口,分配內核對象,關聯類,返回 HWND。 |
| 顯示更新 | ShowWindow/UpdateWindow | 使窗口可見,發送首個 WM_PAINT 觸發繪製。 |
| 消息循環 | GetMessage/TranslateMessage/DispatchMessage | 程序靈魂:從隊列取消息、鍵盤轉換、分發到窗口過程。GetMessage 遇 WM_QUIT 返回 0 退出循環。 |
| 窗口過程 | MyWinProc (回調) | 處理針對該窗口的所有消息。DefWindowProc 提供默認行為 (如標題欄拖動、系統菜單)。 |
TForm 構造函數 → 隱式調用 RegisterClass (類名如 TForm1)、CreateWindowEx (風格 WS_POPUP 等)。Application.Run → 封裝 GetMessage/DispatchMessage 循環,並處理 TApplication 級別消息 (如 OnIdle, OnMessage、異常捕獲)。TControl.WndProc → 封裝 MyWinProc,將消息分發為 OnClick/OnPaint 等事件、屬性存取、子控件佈局。TFont/TCanvas 封裝)。WM_CREATE 創建 GDI 對象,必須在 WM_DESTROY 刪除 (DeleteObject) 或存入窗口實例數據 (SetWindowLongPtr(hWnd, GWLP_USERDATA, ...))。PChar (即 PWideChar),系統調用 RegisterClassExW/CreateWindowExW。資源字符串需一致。WM_PAINT 必須 BeginPaint/EndPaint 配對,否則會無限重繪導致 CPU 100%。DefWindowProc 必須調用處理未關心消息,否則窗口無法移動、關閉、響應系統命令。{$APPTYPE CONSOLE}) 也可創建 GUI 窗口,但需自行實現消息循環。純 API 窗口程序雖代碼量大、細節多,但它剝離了 VCL 厚重封裝,展現了 Windows 圖形子系統的核心架構:**類註冊 → 實例創建 → 消息泵 → 窗口過程**。每位 Delphi 開發者至少應手寫一次,方能真正理解「事件驅動」、「句柄」、「消息分發」等核心概念,並在需要時突破 VCL 限制實現極致控制。