// 錯誤範例:在建構對話框物件後、DoModal 前直接設定控制項
void CMyView::OnShowDialog()
{
CMyDialog dlg; // 1. 建構對話框物件
dlg.m_btnOK.SetWindowText(_T("確定送出")); // 2. 嘗試修改按鈕文字 → ASSERT 失敗!m_hWnd 為 NULL
dlg.DoModal(); // 3. 顯示模態對話框
}
除錯斷點停在 ASSERT(::IsWindow(m_hWnd)),m_hWnd == 0x00000000。原因:**控制項視窗尚未建立**。
| 階段 | 動作 | 控制項視窗狀態 |
|---|---|---|
建構函數 CMyDialog() | 初始化 C++ 物件成員變數 | 不存在 (m_hWnd = NULL) |
DoModal() / Create() | 建立視窗、載入對話框模板、建立所有子控制項視窗、呼叫 OnInitDialog | 建立完成 (m_hWnd 有效) |
OnInitDialog() | 視窗建立後、顯示前的初始化邏輯 | 可安全操作 |
| 對話框運行中 | 使用者互動 | 可安全操作 |
OnDestroy / PostNcDestroy | 視窗銷毀 | 失效 (m_hWnd 變 NULL) |
| 錯誤代碼 | 問題 | 修正 |
|---|---|---|
dlg.m_btn.SetText(...); dlg.DoModal(); | 建構後視窗未建立 | 移至 OnInitDialog 或 Create 後 |
dlg.Create(...); dlg.m_btn.SetText(...); dlg.ShowWindow(SW_SHOW); | 正確 (非模態) | 無 |
dlg.DoModal(); dlg.m_btn.SetText(...); | DoModal 返回後視窗已銷毀 | 在 OnInitDialog 或對話框運行時 (訊息處理) 修改 |
| 全域變數傳遞資料 | 耦合度高、執行緒不安全、難維護 | 改用 Setter 函式、建構函數參數、DDX 資料成員 |
// 正確的非模態對話框成員變數管理 (View/Frame 中)
class CMyView : public CView
{
CMyDialog* m_pModelessDlg = nullptr; // 指標成員
void OnShowModeless()
{
if (m_pModelessDlg) { m_pModelessDlg->SetFocus(); return; } // 已開啟則聚焦
m_pModelessDlg = new CMyDialog(this);
if (m_pModelessDlg->Create(IDD_MYDIALOG, this))
{
m_pModelessDlg->ShowWindow(SW_SHOW);
}
else
{
delete m_pModelessDlg;
m_pModelessDlg = nullptr;
}
}
// 視圖銷毀時確保關閉非模態對話框
void OnDestroy()
{
if (m_pModelessDlg)
{
m_pModelessDlg->DestroyWindow(); // 觸發 PostNcDestroy
m_pModelessDlg = nullptr;
}
CView::OnDestroy();
}
};
// MyDialog.cpp - 自動刪除機制
void CMyDialog::PostNcDestroy()
{
CDialog::PostNcDestroy();
delete this; // 釋放 new 配置的記憶體
}
MFC 對話框控制項操作的鐵律:**視窗建立完成後 (OnInitDialog / Create 返回 TRUE 後) 才能操作**。掌握 OnInitDialog (模態/非模態通用)、Create() 後 (非模態專用)、DDX/DDV 資料成員 (解耦合) 三大正確時機,配合 PostNcDestroy 自動釋放非模態對話框記憶體,即可避開 99% 的 ASSERT(IsWindow) 坑點。拒絕全域變數傳遞資料,擁抱封裝與 RAII。