MFC 繪圖設備上下文 (DC) 兩大常用類:CDC (基類) 和 CClientDC (客戶區 DC),核心區別在於獲取方式與生命周期管理。
| 特性 | CDC (配合 GetDC/ReleaseDC) | CClientDC |
|---|---|---|
| 獲取方式 | CDC* pDC = GetDC(); 或 CDC dc; dc.Attach(GetDC()); | CClientDC dc(this); 構造時自動 GetDC() |
| 釋放責任 | 手動 ReleaseDC(pDC); 或 dc.Detach(); ReleaseDC(hDC); | 自動 析構函數自動 ReleaseDC() |
| 適用場景 | 長時間持有 DC、跨函數傳遞、非 WM_PAINT 繪圖 | 短時繪圖、函數局部變量、RAII 風格 |
| 構造開銷 | 需顯式調用 | 構造即獲取,析構即釋放 |
| 繪圖函數 | 所有 GDI 函數 | 所有 GDI 函數 (繼承 CDC) |
void CMyView::OnDraw(CDC* pDC) {
// 框架傳入的 pDC:由框架管理,不釋放!
pDC->Rectangle(10, 10, 100, 100);
}
void CMyView::OnLButtonDown(UINT nFlags, CPoint point) {
CDC* pDC = GetDC(); // 獲取
if (pDC) {
pDC->SetPixel(point, RGB(255,0,0));
ReleaseDC(pDC); // 必須釋放,否則 GDI 泄漏
}
// 或用栈對象管理
CClientDC dc(this); // 構造獲取
dc.SetPixel(point, RGB(0,255,0));
// 離開作用域自動釋放
}
void DrawSomething(CWnd* pWnd) {
CClientDC dc(pWnd); // 構造:::GetDC(pWnd->m_hWnd)
CRect rect;
pWnd->GetClientRect(&rect);
dc.FillSolidRect(&rect, RGB(240,240,240));
CPen pen(PS_SOLID, 2, RGB(0,0,255));
CPen* pOldPen = dc.SelectObject(&pen);
dc.MoveTo(0, 0);
dc.LineTo(100, 100);
dc.SelectObject(pOldPen); // 恢復舊筆刷,防止 GDI 物件泄漏
// 函數結束,dc 析構 → ::ReleaseDC(pWnd->m_hWnd, dc.m_hDC)
}
Detach() 再 ReleaseDC(),否則雙重釋放崩潰CPaintDC (BeginPaint/EndPaint),不能用 GetDC| 錯誤代碼 | 後果 |
|---|---|
CDC* pDC = GetDC(); ... // 忘記 ReleaseDC | GDI 句柄泄漏,繪圖失效,系統變慢,最終崩潰 |
CClientDC dc(this); dc.SelectObject(&pen); // 忘記恢復舊筆 | 筆刷物件泄漏,下次繪圖樣式錯亂 |
CDC dc; dc.Attach(GetDC()); ... dc.Detach(); // 忘記 ReleaseDC | 同第一條 |
CPaintDC dc(this); // 在非 WM_PAINT 用 | BeginPaint 失敗,繪圖無效 |
口訣:「CClientDC 靠譜,構造析構全自動;GetDC 要小心,釋放別忘記;SelectObject 存舊指針,用完還原最關鍵。」