Windows 應用中,視窗由 UI 線程 創建。它既是線程(有線程函數、ID),又很特殊:線程函數創建視窗與控件,並負責訊息循環。
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (bRet == -1) { /* 錯誤退出 */ }
else {
TranslateMessage(&msg); // WM_KEYDOWN/UP → WM_CHAR
DispatchMessage(&msg); // 依視窗句柄分發
}
}
GetMessage:從線程訊息佇列取訊息TranslateMessage:鍵盤訊息轉換DispatchMessage:關鍵——依句柄分發給對應視窗LRESULT CALLBACK MainWndProc(...) {
switch (uMsg) {
case WM_CREATE: /* 初始化 */ return 0;
case WM_PAINT: /* 繪製 */ return 0;
default: return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
每個視窗/控件一個視窗過程,多分支處理訊息。控件本質也是「Window」,擁有獨立視窗過程。
關鍵:每個 UI 線程一個佇列(非每視窗一個)。只有調用 GDI/User API 時,OS 才創建佇列。
static void Main() {
var frm = new Form1();
frm.Show(); // 非阻塞,無訊息循環
Console.ReadKey(); // 阻塞主線程
}
原因:主線程有佇列但無訊息循環,Show() 返回即阻塞於 ReadKey(),訊息堆積無人分發,視窗無法繪製/響應。
static void ShowWindow() {
var frm = new Form1();
frm.ShowDialog(); // 內建訊息循環,阻塞直到關閉
}
新線程跑 ShowDialog(),主線程可繼續,雙 UI 線程共存:
將 Console App 改為 Windows Application(無控制台視窗)後,Console.ReadKey() 會拋異常:主線程不再是 UI 線程,無佇列,底層 Win32 API 失效。
理解 UI 線程、訊息佇列、訊息循環、系統投遞鏈路,是多線程 UI 開發的基石。錯誤歡迎指正。