首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC++

《VC++技術內幕》學習筆記(五):視窗生命周期五大關鍵消息深度解析 (WM_CREATE 到 WM_NCDESTROY)

     編輯:VC++

Day 5 核心主題:基本事件處理 — 視窗生命周期

MFC 將 Windows 原始消息封裝為 OnXxx 虛函數,視圖類 (CView) 與框架窗口類 (CFrameWnd) 均可重寫。以下五大消息貫穿視窗從誕生到銷毀的全過程,**必須精通**。

1. WM_CREATE → OnCreate(LPCREATESTRUCT lpCS)

  • **觸發時機**:CWnd::Create 內部調用 CreateWindowEx 後,窗口尚未可見、子控件未創建、DC 未就緒。
  • **返回值**:0 繼續創建;-1 銷毀窗口並返回創建失敗。
  • **禁忌**:**不可**調用依賴窗口完全激活的 API (如 GetClientRect, SetTimer, CreateChildControls)。
  • **替代方案**:在 OnInitialUpdate (視圖) 或 OnPostCreate (框架) 中執行需窗口就緒的初始化。注意 SDI 應用中 OnInitialUpdate 可能被多次調用。

2. WM_CLOSE → OnClose()

  • **觸發時機**:用戶點擊關閉按鈕、系統菜單「關閉」、Alt+F4、Task Manager「結束任務」。
  • **控制權**:重寫 OnClose 可**完全攔截關閉流程** — 彈出「保存確認」、最小化到托盤、延遲關閉等。
  • **替代實現**:重寫 CDocument::SaveModified 同樣可在關閉前提示保存 (文檔級)。
  • **關鍵**:若允許關閉,**必須調用基類 CWnd::OnClose** (內部發送 WM_DESTROY),否則窗口不會銷毀。

3. WM_QUERYENDSESSION → OnQueryEndSession()

  • **觸發時機**:用戶登出/關機/重啟,或程式調用 ExitWindowsEx。系統向**所有頂層窗口**廣播。
  • **返回值**:TRUE 允許關機;FALSE 取消關機 (如有未保存數據強制攔截)。
  • **後續**:若所有窗口返回 TRUE,系統發送 WM_ENDSESSION (wParam=TRUE) 通知確定關機;否則發送 WM_ENDSESSION (wParam=FALSE) 取消。

4. WM_DESTROY → OnDestroy()

  • **觸發時機**:WM_CLOSE 後,DestroyWindow 內部發送。窗口已從螢幕移除,但**進程仍存在**、句柄仍有效、子窗口正在銷毀中。
  • **用途**:釋放窗口級資源 (筆刷、字體、位圖、定時器、鉤子、線程句柄)、保存窗口位置/狀態到註冊表/INI。
  • **鐵律**:**必須調用基類 CWnd::OnDestroy** (內部發送 WM_NCDESTROY、清理內部映射表)。**不可**在這裡調用 PostQuitMessage (那屬於 OnClose 的職責)。

5. WM_NCDESTROY → OnNcDestroy()

  • **觸發時機**:窗口**最後**一條消息。非客戶區 (標題欄、邊框、菜單) 已銷毀,窗口句柄即將失效,CWnd 對象即將刪除。
  • **用途**:釋放**不依賴窗口句柄**的最終資源、從全域映射表移除 HWND→CWnd* 關聯 (MFC 內部自動完成)。
  • **鐵律**:**必須調用基類 CWnd::OnNcDestroy** (內部執行 delete this 對於動態創建的窗口對象)。

生命周期時序圖

用戶點擊關閉按鈕
       │
       ▼
WM_CLOSE → OnClose() [可攔截] → 調用基類
       │
       ▼
WM_DESTROY → OnDestroy() [釋放資源] → 調用基類
       │
       ▼
WM_NCDESTROY → OnNcDestroy() [最終清理] → 調用基類 → delete this
       │
       ▼
PostQuitMessage(0) → 消息循環結束 → WinMain 返回 → 進程退出

惡作劇程式:右鍵觸發系統關機

void CMy007View::OnDraw(CDC* pDC) {
    pDC->TextOut(100, 100, "請不要單擊鼠標右鍵,否則後果自負!");
}

void CMy007View::OnRButtonDown(UINT nFlags, CPoint point) {
    ::MessageBox(NULL, "你確定要這樣做嗎?", "警告", MB_OK);
    ::ExitWindowsEx(EWX_SHUTDOWN | EWX_FORCE, 0); // 強制關機
}

**原理**:ExitWindowsEx 廣播 WM_QUERYENDSESSION,若無程式攔截則強制關機。展示了消息廣播機制的威力。

常見錯誤與最佳實踐

錯誤後果正確做法
OnCreate 中調用 GetClientRect返回全零矩形,導致佈局錯誤移至 OnInitialUpdate / OnSize
OnClose 不調用基類窗口永遠不關閉,進程殭屍最後調用 CWnd::OnClose()
OnDestroy 中 PostQuitMessage多文檔應用關閉一個文檔就退出整個程式只在主框架 OnClose 中調用
OnNcDestroy 不調用基類CWnd 對象記憶體洩漏,HWND 映射表殘留最後調用 CWnd::OnNcDestroy()

雷神心法:在真實專案中驗證

  1. 用 Spy++ 監控關閉過程中的消息序列,確認順序與參數。
  2. 在每個 OnXxx 加 TRACE 輸出時間戳,觀察調用堆疊。
  3. 故意註釋基類調用,觀察崩潰/洩漏現象,加深記憶。

下一篇預告

Day 6:映射模式大全 — MM_TEXT, MM_HIMETRIC, MM_ANISOTROPIC... 邏輯座標與設備座標轉換、滾動視圖 CScrollView 實戰、滾動條鍵盤控制。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved