前言
Win32 程式設計中,GDI 物件、核心物件、記憶體區塊等資源通常需配對釋放 (DeleteObject, CloseHandle, GlobalFree),但有幾類資源 系統自動管理,呼叫端無需 (且不應) 釋放。本文列舉這些特例並說明原因。
無需釋放的資源清單
| 函數/資源 | 取得方式 | 原因 | 注意事項 |
LoadCursor | 載入游標資源 | 系統快取,進程結束自動釋放 | 不可呼叫 DestroyCursor |
LoadIcon | 載入圖示資源 | 系統快取,進程結束自動釋放 | 不可呼叫 DestroyIcon (共享圖示) |
LoadString | 載入字串資源 | 複製到呼叫端緩衝區,非物件控制代碼 | 緩衝區由呼叫端管理 (棧/堆) |
GetStockObject | 取得預設 GDI 物件 | 系統全域單例,永久存在 | 不可呼叫 DeleteObject |
詳細說明
1. LoadCursor / LoadIcon — 共享資源快取
hCursor = LoadCursor(NULL, IDC_ARROW) ; 系統標準游標
hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYICON))
- 系統維護資源快取表,相同參數重複呼叫返回同一控制代碼。
- 進程終止時系統統一清理,呼叫
DestroyCursor/Icon 反而會破壞快取邏輯,導致其他元件使用失效控制代碼。
- 例外:
CreateIconIndirect、CopyIcon、LoadImage(LR_DEFAULTSIZE | LR_SHARED) 產生的非共享物件 必須 配對銷毀。
2. LoadString — 字串複製而非物件
char szBuf[256];
LoadString(hInstance, IDS_MYSTRING, szBuf, 256)
- 函數將資源區字串複製到呼叫端提供的緩衝區,返回複製字元數。
- 無控制代碼、無核心物件,自然無釋放概念。緩衝區生命週期由呼叫端控制 (區域變數/堆積/靜態)。
3. GetStockObject — 系統全域單例
hPen = GetStockObject(BLACK_PEN)
hBrush = GetStockObject(WHITE_BRUSH)
hFont = GetStockObject(SYSTEM_FONT)
- 返回預設 GDI 物件的永久控制代碼,全系統共用單一實例。
- 物件由系統建立於啟動期,銷毀於關機期,應用程式無權管理。
- 呼叫
DeleteObject(hPen) 會失敗 (返回 0) 且可能破壞其他元件繪圖。
常見錯誤模式
; 錯誤:嘗試釋放共享資源
hCursor = LoadCursor(NULL, IDC_WAIT)
; ... 使用 ...
DestroyCursor(hCursor) ; ❌ 錯誤!破壞系統快取
; 錯誤:釋放標準物件
hBrush = GetStockObject(GRAY_BRUSH)
DeleteObject(hBrush) ; ❌ 錯誤!標準刷不可刪除
對比:必須釋放的資源
| 資源類型 | 建立函數 | 釋放函數 |
| 筆刷 | CreatePen, CreateSolidBrush | DeleteObject |
| 字型 | CreateFont, CreateFontIndirect | DeleteObject |
| 位圖 | CreateBitmap, CreateCompatibleBitmap | DeleteObject |
| 圖示/游標 (非共享) | CreateIconIndirect, CreateCursor | DestroyIcon, DestroyCursor |
| 裝置內容 | GetDC, CreateCompatibleDC | ReleaseDC, DeleteDC |
| 記憶體 | GlobalAlloc, HeapAlloc, VirtualAlloc | GlobalFree, HeapFree, VirtualFree |
| 核心物件 | CreateFile, CreateThread, CreateEvent | CloseHandle |
最佳實務建議
- 默認原則:誰建立誰釋放;
LoadXxx/GetStockObject 非建立函數,不負責釋放。
- RAII 模式:C++ 使用智慧指標 (
std::unique_ptr with custom deleter) 自動管理;組合語言建立結構化清理標籤 (cleanup:) 確保所有路徑釋放。
- 工具驗證:使用 GDIView、HandleEx、Application Verifier 偵測洩漏與雙重釋放。
總結
LoadCursor、LoadIcon、LoadString、GetStockObject 取得的資源屬於系統管理的共享/快取/單例類別,呼叫端無需也不可釋放。混淆「建立」與「取得」是資源洩漏與崩潰的常見根因。建立清晰的資源所有權心智模型,是穩健 Win32 程式設計的基石。
- 上一篇文章: 組合語言結構體存取最佳化:直接型別轉換取代 ASSUME 指令
- 下一篇文章: x86 保護模式核心架構:控制暫存器 (CR0-CR3) 與系統位址暫存器 (GDTR/IDTR/LDTR/TR) 完整解析
Copyright ©
程式碼之家 All Rights Reserved