C 語言以 main() 為入口,Windows GUI 應用程式則以 WinMain() 為入口。MFC 將此封裝於框架內,但理解原生 WinMain 流程對於掌握視窗機制至關重要。本文手把手教學撰寫完整的 WinMain 程式,涵蓋視窗類別註冊、視窗建立、訊息迴圈、視窗程序四大核心環節。
#include <windows.h>
#include <stdio.h>
LRESULT CALLBACK WinMyProc(
HWND hWnd, // 視窗控制代碼
UINT uMsg, // 訊息識別碼
WPARAM wParam, // 訊息參數 1
LPARAM lParam // 訊息參數 2
);
int WINAPI WinMain(
HINSTANCE hInstance, // 目前實例控制代碼
HINSTANCE hPrevInstance, // 前一實例 (Win32 恆為 NULL)
LPSTR lpCmdLine, // 命令列參數
int nCmdShow // 顯示狀態 (SW_SHOWNORMAL 等)
)
{
// 1. 定義並填寫視窗類別結構
WNDCLASS wndcls = {0};
wndcls.cbClsExtra = 0;
wndcls.cbWndExtra = 0;
wndcls.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 白色背景
wndcls.hCursor = LoadCursor(NULL, IDC_ARROW); // 標準箭頭
wndcls.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Windows Logo 圖示
wndcls.hInstance = hInstance; // 應用程式實例
wndcls.lpfnWndProc = WinMyProc; // 視窗程序指標
wndcls.lpszClassName = "BEYOND"; // 類別名稱
wndcls.lpszMenuName = NULL; // 無選單
wndcls.style = CS_HREDRAW | CS_VREDRAW; // 水平/垂直重繪
// 2. 註冊視窗類別
if (!RegisterClass(&wndcls))
return 0; // 註冊失敗
// 3. 建立視窗
HWND hWnd = CreateWindow(
"BEYOND", // 類別名稱
"LoveBeyond", // 視窗標題
WS_OVERLAPPEDWINDOW, // 標準重疊視窗樣式
0, 0, // 預設位置
600, 400, // 寬、高
NULL, // 父視窗
NULL, // 選單
hInstance, // 實例
NULL // 額外參數
);
if (!hWnd)
return 0;
// 4. 顯示與更新視窗
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// 5. 訊息迴圈
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) // 收到 WM_QUIT 回傳 FALSE 跳出
{
TranslateMessage(&msg); // 處理鍵盤訊息 (WM_KEYDOWN → WM_CHAR)
DispatchMessage(&msg); // 分派至視窗程序
}
return (int)msg.wParam; // 返回退出碼
}
LRESULT CALLBACK WinMyProc(
HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CHAR: // 字元輸入 (經 TranslateMessage 產生)
{
char szChar[20];
sprintf(szChar, "char is %d", wParam); // wParam 為字元碼
MessageBox(hWnd, szChar, "Keyboard", MB_OK);
break;
}
case WM_LBUTTONDOWN: // 滑鼠左鍵按下
MessageBox(hWnd, "Mouse clicked", "Mouse", MB_OK);
break;
case WM_PAINT: // 視窗重繪
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hWnd, &ps);
TextOut(hDC, 0, 0, "LoveBeyond", strlen("LoveBeyond"));
EndPaint(hWnd, &ps);
break;
}
case WM_CLOSE: // 使用者點擊關閉鈕
if (IDYES == MessageBox(hWnd, "想要結束嗎?", "Confirm", MB_YESNO))
{
DestroyWindow(hWnd); // 發送 WM_DESTROY
}
break;
case WM_DESTROY: // 視窗銷毀
PostQuitMessage(0); // 發送 WM_QUIT 結束訊息迴圈
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam); // 預設處理
}
return 0;
}
| 階段 | 關鍵 API | 說明 |
|---|---|---|
| 類別註冊 | RegisterClass | 向系統登記視窗類別屬性,lpfnWndProc 指定處理函數 |
| 視窗建立 | CreateWindow | 依類別建立視窗實例,回傳 HWND |
| 顯示更新 | ShowWindow/UpdateWindow | 使視窗可見並觸發首次 WM_PAINT |
| 訊息迴圈 | GetMessage/TranslateMessage/DispatchMessage | 核心事件迴圈,WM_QUIT 時結束 |
| 視窗程序 | WndProc | 處理特定訊息,未處理者交給 DefWindowProc |
WM_CREATE:視窗建立時 (初始化資源)WM_DESTROY:視窗銷毀時 (釋放資源,發送 PostQuitMessage)WM_PAINT:需重繪客戶區 (BeginPaint/EndPaint 配對)WM_CLOSE:使用者要求關閉 (可攔截詢問)WM_COMMAND:選單/控制項通知WM_KEYDOWN/WM_CHAR:鍵盤輸入WM_MOUSEMOVE/WM_LBUTTONDOWN:滑鼠事件MFC 在 AfxWinMain 中完成上述流程:
AfxWinInit 初始化 MFC 狀態CWinApp::InitApplication、InitInstance (建立主視窗)CWinThread::Run 訊息迴圈 (含 OnIdle 處理)AfxWinTerm 清理theApp 全域物件在 WinMain 前建構,提供應用程式單一入口。
手寫 WinMain 能深入理解 Windows 視窗機制核心:類別註冊 → 實例建立 → 訊息迴圈 → 程序分派。這是所有 Windows GUI 程式設計的基石,MFC、WTL、Qt 等框架皆在此模型之上封裝。建議初學者親自撰寫並除錯一次,體會 WM_PAINT 重繪機制與訊息迴圈運作。