在 MFC 專案中,`OnInitDialog()` 負責對話方塊初始化。若初始化邏輯龐大(如載入大量資料、網路請求、複雜計算),會導致對話方塊長時間無法顯示(卡住 2 秒以上),使用者體驗極差。解決核心在於:**讓對話方塊先繪製顯示(`OnInitDialog` 快速返回),再於背景/閒置時執行耗時任務。**
利用 `SetTimer` 在 `OnInitDialog` 返回前啟動一個極短週期(如 100ms)的定時器。對話方塊顯示後,訊息迴圈開始運作,`WM_TIMER` 訊息觸發 `OnTimer`,執行耗時任務並立即銷毀定時器。
// 標頭檔宣告成員變數
UINT_PTR m_nTimerID;
// 實作檔
BOOL CMyAppDlg::OnInitDialog() {
CDialog::OnInitDialog();
// ... 其他快速初始化 ...
// 設定 100ms 後觸發一次
m_nTimerID = SetTimer(1, 100, NULL);
return TRUE;
}
void CMyAppDlg::OnTimer(UINT_PTR nIDEvent) {
if (nIDEvent == m_nTimerID) {
KillTimer(m_nTimerID); // 僅執行一次
m_nTimerID = 0;
// 在這裡執行耗時初始化任務
AfxMessageBox(_T("Hello MFC - 耗時任務執行中..."));
return;
}
CDialog::OnTimer(nIDEvent);
}
優點:實作簡單,無需定義新訊息。
缺點:精度受限於系統定時器解析度(約 10-15ms);若任務極長仍會阻塞 UI 執行緒(建議配合 Worker Thread)。
在 `OnInitDialog` 結束前 `PostMessage` 一個自訂訊息(`WM_USER` 以上)。訊息會排入佇列,待 `OnInitDialog` 返回、視窗建立完成、訊息迴圈啟動後,自動派發至對應處理函式。
// 定義自訂訊息 (WM_USER 範圍 0x0400 ~ 0x7FFF)
#define WM_MY_PRIVATE (WM_USER + 1551)
// 類別宣告 (標頭檔)
afx_msg LRESULT OnPrivateMessage(WPARAM wParam, LPARAM lParam);
// 訊息映射 (實作檔)
BEGIN_MESSAGE_MAP(CMyAppDlg, CDialog)
ON_MESSAGE(WM_MY_PRIVATE, &CMyAppDlg::OnPrivateMessage)
END_MESSAGE_MAP()
BOOL CMyAppDlg::OnInitDialog() {
CDialog::OnInitDialog();
// ... 其他快速初始化 ...
// 將訊息張貼到佇列尾端
PostMessage(WM_MY_PRIVATE);
return TRUE;
}
LRESULT CMyAppDlg::OnPrivateMessage(WPARAM wParam, LPARAM lParam) {
// 對話方塊已顯示,安全執行耗時操作
AfxMessageBox(_T("Hello MFC - PostMessage 方式執行"));
return 0;
}
優點:語意清晰(明確表示「初始化後執行」),不依賴定時器精度,易於擴充參數(`wParam`/`lParam` 傳遞資料)。
缺點:需額外定義訊息 ID、映射宏與處理函式,代碼量稍多。
上述兩種方案的任務仍在 **UI 執行緒** 執行。若任務極度耗時(> 500ms),介面仍會短暫凍結。最佳實踐:
| 特性 | SetTimer 方案 | PostMessage 方案 |
|---|---|---|
| 實作複雜度 | 低 | 中 |
| 觸發時機 | 約 100ms 後 | 訊息迴圈啟動後即刻 |
| 參數傳遞 | 需用成員變數 | 支援 wParam/lParam |
| 適用場景 | 快速原型、輕量延遲 | 正式專案、需傳參、語意明確 |
推薦採用 PostMessage + Worker Thread 組合,兼顧回應速度與介面流暢度。