Windows 應用程式進入訊息迴圈後,直到收到 WM_QUIT 才真正結束。常見觸發路徑:
WM_SYSCOMMAND, SC_CLOSE → WM_CLOSE。ID_APP_EXIT → CWinApp::OnAppExit() → SendMessage(WM_CLOSE)。PostQuitMessage(0) 直接貼上 WM_QUIT(繞過 WM_CLOSE,不推薦)。void CWinApp::OnAppExit()
{
ASSERT(m_pMainWnd != NULL);
m_pMainWnd->SendMessage(WM_CLOSE); // 同步發送,確保關閉流程完成
}
設計為 SendMessage 而非 PostMessage,確保主視窗關閉邏輯(詢問儲存、釋放資源)在 OnAppExit 返回前完成。
適用於 SDI 主框架、MDI 主框架 (CMDIFrameWnd)、MDI 子框架 (CMDIChildWnd)。流程圖:
m_lpfnCloseProc 非空時呼叫(列印預覽等特殊模式)。// 簡化版 CFrameWnd::OnClose 主視窗分支
void CFrameWnd::OnClose()
{
if (this == AfxGetApp()->m_pMainWnd) {
// 1. 關閉所有開啟文檔 (會觸發各文檔的 SaveModified、DeleteContents)
CDocManager* pDocMgr = AfxGetApp()->m_pDocManager;
if (pDocMgr) pDocMgr->CloseAllDocuments(FALSE); // FALSE = 不提示儲存?實際會提示
// 2. 銷毀主框架視窗 (觸發 WM_DESTROY → PostQuitMessage)
DestroyWindow();
}
else { /* MDI 子視窗分支見下 */ }
}
CloseAllDocuments 會逐一呼叫 CDocument::OnCloseDocument → SaveModified (詢問儲存) → DeleteContents → 移除視圖/框架。
// MDI 子視窗分支
void CMDIChildWnd::OnClose()
{
CDocument* pDoc = GetActiveDocument();
if (!pDoc) { DestroyWindow(); return; }
// 計算有多少視圖/框架正在顯示此文檔
int nViewCount = 0;
POSITION pos = pDoc->GetFirstViewPosition();
while (pos) {
CView* pView = pDoc->GetNextView(pos);
if (pView->GetParentFrame() != this) nViewCount++;
}
if (nViewCount == 0) {
// 此文檔僅被本視窗顯示 → 關閉文檔 (含儲存提示) + 銷毀視窗
pDoc->OnCloseDocument();
} else {
// 文檔還被其他視窗顯示 → 僅銷毀本視窗,文檔保留
DestroyWindow();
}
}
// CWnd::OnDestroy 處理 WM_DESTROY
void CWnd::OnDestroy()
{
// 清理視窗相關資源
if (m_hWnd == AfxGetApp()->m_pMainWnd->m_hWnd) {
// 主視窗被銷毀 → 發送 WM_QUIT 結束訊息迴圈
PostQuitMessage(0);
}
// 釋放 MFC 內部資源 (HandleMap 移除等)
Detach();
}
CWinApp::Run() 訊息迴圈:
int CWinApp::Run()
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
if (!PreTranslateMessage(&msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam; // WM_QUIT 的 wParam (通常 0)
}
| 需求 | 覆寫位置 | 注意事項 |
|---|---|---|
| 攔截關閉、詢問確認 | CWnd::OnClose() 或 OnQueryEndSession() | 呼叫 DestroyWindow() 繼續流程,或直接 return 取消關閉。 |
| 文檔儲存前檢查 | CDocument::SaveModified() | 回傳 TRUE 允許關閉,FALSE 取消。預設彈出「是/否/取消」對話框。 |
| 文檔銷毀時清理資料 | CDocument::DeleteContents() | 釋放文檔持有的記憶體、關閉資料庫連線等。 |
| 應用程式級清理 | CWinApp::ExitInstance() | 訊息迴圈結束後呼叫,釋放全域資源、寫入登錄設定。 |
| 強制立即退出 (不提示) | AfxGetApp()->m_pMainWnd->DestroyWindow() | 繞過 OnClose,慎用。或 PostQuitMessage 直接結束迴圈。 |
DoModal() 正在執行,主視窗 DestroyWindow 不會關閉對話框。需在 OnClose 中 EndDialog 所有模態對話框,或使用 PostMessage(WM_CLOSE) 讓對話框自行處理。ExitInstance 前務必 WaitForSingleObject 等待工作執行緒結束,否則進程殘留。CDocument::m_bAutoDelete (預設 TRUE) 及視圖計數邏輯。CWnd::OnQueryEndSession 返回 TRUE 允許關機,FALSE 阻止。限時 5 秒必須返回。MFC 退出流程是一個層層遞進的協作過程:使用者動作 → WM_CLOSE → CFrameWnd::OnClose → (主視窗)關閉所有文檔 → 銷毀主視窗 → WM_DESTROY → PostQuitMessage → 訊息迴圈結束 → ExitInstance。掌握每個階段的覆寫點、預設行為、執行順序,能讓您實現「優雅退出、資料不遺失、資源不洩漏」的專業級應用程式。