| 類別 | 功能 | 典型構造方式 | Windows 句柄類型 |
| CPen | 畫線、框架邊框 (顏色、寬度、樣式) | CPen pen(PS_SOLID, 2, RGB(255,0,0)); | HPEN |
| CBrush | 填充區域 (實心、斜線、圖案、位圖) | CBrush brush(RGB(0,255,0)); | HBRUSH |
| CFont | 字體 (字型、大小、粗細、斜體、字符集) | CFont font; font.CreateFont(...); (兩階段構造) | HFONT |
| CBitmap | 位圖 (像素矩陣、兼容位圖、DIB) | CBitmap bmp; bmp.CreateCompatibleBitmap(pDC, w, h); | HBITMAP |
| CPalette | 調色板 (256 色映射表、實現調色板動畫) | CPalette pal; pal.CreatePalette(&lpLogPal); | HPALETTE |
| CRgn | 區域 (矩形、橢圓、多邊形、組合) | CRgn rgn; rgn.CreateEllipticRgn(...); | HRGN |
**注意**:CFont 和 CRgn **必須**先默認構造,再調用 CreateXxx;其他四類可直接用參數構造函數一次完成。
SelectObject:選入/選出 — 「保存舊、用新、還原舊」鐵律
void CMyView::OnDraw(CDC* pDC) {
pDC->MoveTo(10,10); pDC->LineTo(110,10); // 預設筆 (黑、1像素、實線)
CPen newPen(PS_DASHDOTDOT, 10, RGB(255,0,0)); // 紅、10像素、雙點劃線
CPen* pOldPen = pDC->SelectObject(&newPen); // 選入新筆,**返回舊筆指針**
pDC->MoveTo(10,20); pDC->LineTo(110,20); // 用新筆畫線
pDC->SelectObject(pOldPen); // **還原舊筆** — 關鍵步驟!
pDC->MoveTo(10,30); pDC->LineTo(110,30); // 回到預設筆
}
**原則**:每次 SelectObject 選入新物件,**必須**在同一 DC 生命週期內選回舊物件,否則 GDI 物件洩漏、DC 狀態污染。
庫存物件:系統預建、永不刪除、直接用
調用 SelectStockObject 即可選入,無需構造、無需刪除、無需還原 (系統忽略刪除請求)。
| 類別 | 常用庫存物件 |
| Brush | WHITE_BRUSH, LTGRAY_BRUSH, GRAY_BRUSH, DKGRAY_BRUSH, BLACK_BRUSH, NULL_BRUSH, HOLLOW_BRUSH |
| Pen | BLACK_PEN, WHITE_PEN, NULL_PEN |
| Font | SYSTEM_FONT, DEFAULT_GUI_FONT, ANSI_VAR_FONT, ANSI_FIXED_FONT, OEM_FIXED_FONT, SYSTEM_FIXED_FONT, DEVICE_DEFAULT_FONT |
| Palette | DEFAULT_PALETTE (系統 20 靜態色) |
句柄持久化技巧:GetSafeHandle + FromHandle
問題:SelectObject 返回的 CGdiObject* 是**臨時指針**,閒置處理時會被框架刪除,不可直接存入成員變數。
解決:轉換為 Windows 句柄 (HGDIOBJ) 長期保存,需要時用 FromHandle 重建臨時 C++ 物件。
// 成員變數
HPEN m_hOldPen;
// 選入新筆並保存舊筆句柄
CPen newPen(PS_SOLID, 5, RGB(0,0,255));
CPen* pOldPen = pDC->SelectObject(&newPen);
m_hOldPen = (HPEN)pOldPen->GetSafeHandle(); // 保存句柄
// ... 繪圖 ...
// 還原:用句柄重建 C++ 物件指針
pDC->SelectObject(CPen::FromHandle(m_hOldPen));
**句柄類型對應表**:
CPen→HPEN, CBrush→HBRUSH, CFont→HFONT, CBitmap→HBITMAP, CPalette→HPALETTE, CRgn→HRGN。
GDI 物件析構鐵律
CGdiObject 有虛擬析構函數,派生類析構時會自動調用 DeleteObject 刪除對應的 Windows GDI 物件。
- **堆疊分配的 GDI 物件** (如函數內部
CPen pen(...)) 會在作用域結束時自動刪除,**前提是已從所有 DC 中選出**。若仍被某 DC 選入,析構時會 ASSERT 失敗 (Debug) 或導致 DC 狀態異常。
- **堆分配 (new)** 的 GDI 物件需手動
delete,同樣需先從 DC 選出。
雷神心法:GDI 物件管理三板斧
- **優先用庫存物件** — 免構造、免刪除、免還原、零洩漏風險。
- **自建物件必配對** —
SelectObject(&newObj) 緊跟保存舊指針,繪圖結束前 SelectObject(pOldObj)。
- **跨函數/跨幀持久化** — 只存句柄 (
GetSafeHandle),用時 FromHandle 重建臨時 C++ 包裹。
(現代專案建議改用 std::shared_ptr 自定義 deleter 封裝 RAII)。
下一篇預告
Day 8:色彩與字體 — RGB 宏、COLORREF、SetTextColor/SetBkColor、CFont::CreateFont 21 參數詳解、TEXTMETRIC 字體度量、可縮放字體繪製實戰 (MM_ANISOTROPIC + Twips)。
- 上一篇文章: 《VC++技術內幕》學習筆記(六):映射模式完全指南 — 從 MM_TEXT 到 MM_ANISOTROPIC、座標轉換、滾動視圖實戰
- 下一篇文章: 《VC++技術內幕》學習筆記(八):色彩與字體 — RGB/COLORREF、CFont::CreateFont 21 參數詳解、可縮放字體繪製實戰
Copyright ©
程式碼之家 All Rights Reserved