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

MFC SetTimer/OnTimer 繪圖代碼不執行的常見原因排查

     編輯:程式設計疑難解答

MFC 中調用 SetTimer 並通過 ClassWizard 添加 OnTimer 響應函數,但定時器觸發時繪圖代碼不執行,通常是消息映射未關聯、繪圖上下文錯誤或定時器 ID 衝突導致。

排查清單(按可能性排序)

1. 消息映射未正確關聯(最常見)

  • 打開 Class Wizard (Ctrl+W) → Message Maps 標籤
  • 選中對話框/視圖類 → 選中 WM_TIMER 消息
  • 點擊 Add Function → 生成 OnTimer(UINT_PTR nIDEvent)
  • 檢查生成的映射宏:

BEGIN_MESSAGE_MAP(CMyDlg, CDialogEx)
    ON_WM_TIMER()
END_MESSAGE_MAP()

2. 定時器 ID 衝突或未啟動


// 啟動定時器:返回值為實際分配的 ID
UINT_PTR nTimerId = SetTimer(1, 1000, NULL); // ID=1, 間隔1秒
if (nTimerId == 0) {
    AfxMessageBox(_T("定時器創建失敗"));
    return;
}
// 成員變量保存 ID,便於 KillTimer
m_nTimerId = nTimerId;
  • 確保 OnTimer 中判斷 nIDEvent == m_nTimerId
  • 檢查是否多次調用 SetTimer 導致 ID 衝突

3. 繪圖代碼上下文錯誤

錯誤做法:在 OnTimer 中直接 CDC* pDC = GetDC(); 繪圖 → 閃爍、丟幀、坐標錯誤

正確做法:


void CMyDlg::OnTimer(UINT_PTR nIDEvent) {
    if (nIDEvent == m_nTimerId) {
        // 1. 標記需要重繪
        InvalidateRect(NULL, FALSE); // 或指定區域 InvalidateRect(&rect, FALSE)
        // 2. 可選:UpdateWindow() 強制立即重繪
        // UpdateWindow(); 
    }
    CDialogEx::OnTimer(nIDEvent);
}

// 繪圖邏輯集中在 OnPaint
void CMyDlg::OnPaint() {
    CPaintDC dc(this); // 自動 BeginPaint/EndPaint
    // 這裡寫所有繪圖代碼
    DrawMyGraphics(dc);
}

4. 定時器在非 UI 線程創建

  • SetTimer 必須在 UI 線程調用,否則消息泵無法分發 WM_TIMER
  • 工作線程需用 PostMessage 通知主線程創建定時器

5. 對話框/視圖未正確創建

  • 模態對話框 DoModal() 運行期間才有消息泵
  • 無模式對話框需確保 Create() 成功且消息泵正常

調試技巧

  1. 在 OnTimer 首行設置斷點,確認是否進入
  2. TRACE(_T("OnTimer: %d\n"), nIDEvent); 輸出調試信息
  3. 使用 Spy++ 監控 WM_TIMER 消息是否送達窗口

最佳實踐模板


class CMyDlg : public CDialogEx {
    UINT_PTR m_nTimerId = 0;
    CRect m_rectDrawArea; // 繪圖區域
    
    afx_msg void OnTimer(UINT_PTR nIDEvent);
    void DrawMyGraphics(CDC& dc);
    DECLARE_MESSAGE_MAP()
};

BEGIN_MESSAGE_MAP(CMyDlg, CDialogEx)
    ON_WM_TIMER()
    ON_WM_PAINT()
END_MESSAGE_MAP()

void CMyDlg::OnTimer(UINT_PTR nIDEvent) {
    if (nIDEvent == m_nTimerId) {
        InvalidateRect(&m_rectDrawArea, FALSE); // 僅刷新繪圖區
    }
    CDialogEx::OnTimer(nIDEvent);
}

void CMyDlg::OnPaint() {
    CPaintDC dc(this);
    DrawMyGraphics(dc);
}

遵循「定時器只負責觸發重繪,OnPaint 統一繪圖」原則,配合 ClassWizard 正確映射消息,定時繪圖即可穩定運行。

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