首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

MFC DateTimePicker 控件導致運行時內存錯誤:DoModal 後 CString 指針被破壞的根因與修復

     編輯:程式設計疑難解答

故障現象

  • SDI 應用,CTestView::OnDraw 中創建 CTestDialog 並 DoModal()
  • 對話框含 CTime m_dateTime 綁定 DateTimePicker (DDX_DateTimeCtrl)
  • 點擊對話框 OK 按鈕關閉後,CTestView::m_strName 內存地址被改寫,導致訪問越界
  • 純 EditBox 版本無此問題

根因深度剖析

DateTimePicker 控件在 DoModal 返回時,觸發了額外的窗口消息處理,導致棧幀異常展開,破壞了調用者棧上的 CString 對象內部指針。

關鍵差異

具體破壞鏈條

  1. DoModal 進入模態消息循環
  2. 用戶點擊 OK → OnOK → UpdateData(TRUE) 執行 DDX
  3. DDX_DateTimeCtrl 內部調用 SendMessage(GDT_VALID) 獲取時間
  4. 若控件處於特定狀態(如無效日期、回調通知),可能觸發重入或棧平衡異常
  5. 返回 DoModal 時,調用者 OnDraw 的棧幀中 m_strName 的 CStringData* 指針已被覆寫

修復方案(按推薦順序)

方案一:將對話框對象提升為成員變量(避免棧上構造/析構)

// TestView.h
class CTestView : public CView {
    CTestDialog m_dlg; // 成員變量,生命周期隨 View
    // ...
};

// TestView.cpp
void CTestView::OnDraw(CDC* pDC) {
    if (m_dlg.DoModal() == IDOK) {
        m_strName = "11111"; // 安全
    }
}

方案二:在對話框中重寫 OnOK 確保數據交換順序正確

void CTestDialog::OnOK() {
    UpdateData(TRUE); // 先從控件取數據
    CDialog::OnOK();  // 再調用基類關閉
}

方案三:檢查項目設置 → 確保「使用 MFC 共享 DLL」與運行時庫一致

  • 屬性頁 → General → Use of MFC: Use MFC in a Shared DLL
  • C/C++ → Code Generation → Runtime Library: Multi-threaded DLL (/MD)(Debug 為 /MDd)
  • 所有項目(主程序、對話框所在庫)必須一致,否則 CString 實現不同導致內存佈局差異

方案四:調試定位具體破壞指令

  1. 在 m_strName 賦值處設置數據斷點(Data Breakpoint):監視 &m_strName 地址變化
  2. 觀察調用棧,定位是哪個 DLL/函數寫入了該地址

最佳實踐總結

  1. 模態對話框優先作為成員變量,避免棧上臨時對象在複雜控件交互中被破壞。
  2. MFC 項目全解決方案統一運行時庫,消除 90% 的內存兼容性問題。
  3. DateTimePicker 等複雜 ActiveX/Common Controls 控件,測試邊界情況**(無效日期、時區變更、DST 切換)。

內存被「莫名其妙」改寫,多半是棧幀平衡被破壞 —— 成員變量化對話框、統一運行時庫,是 MFC 開發的雙重保險。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
控件類型DDX 函數內部實現特點
EditBoxDDX_Text簡單文本交換,無額外窗口子類化
DateTimePickerDDX_DateTimeCtrl涉及 CDateTimeCtrl 子類化、NMC_DATETIMECHANGE 通知、SYSTEMTIME 結構體轉換