1.GDI物件以及釋放方法:
GDI物件 |
產生方法 |
銷燬方法 |
點陣圖(HBITMAP) |
CreateBitmap,CreateBitmapIndirect, CreateCompatibleBitmap,CreateDIBitmap, CreateDIBSection,CreateDiscardableBitmap |
DeleteObject |
畫刷(HBRUSH) |
CreateBrushIndirect,CreateDIBPatternBrush, CreateDIBPatternBrushPt,CreateHatchBrush, CreatePatternBrush,CreateSolidBrush |
DeleteObject |
裝置上下文(HDC) |
CreateDC |
DeleteDC,ReleaseDC |
字型(HFONT) |
CreateFont,CreateFontIndirect |
DeleteObject |
記憶體DC(HDC) |
CreateCompatibleDC |
DeleteDC |
調色盤(HPALETTE) |
CreatePalette |
DeleteObject |
畫筆(HPEN) |
CreatePen,CreatePenIndirect |
DeleteObject |
區域(HRGN) |
CombineRgn,CreateEllipticRgn, CreateEllipticRgnIndirect,CreatePolygonRgn, CreatePolyPolygonRgn,CreateRectRgn, CreateRectRgnIndirect,CreateRoundRectRgn, |
DeleteObject |
2.資源切換時容易出現的GDI洩漏:
1)SelectObject、SetBitmap、SetIcon、SendMessage(訊息為BM_Bitmap時),會返回之前使用的GDI資源,不再使用的GDI資源需要及時釋放(記錄好之前使用的系統GDI資源,在結束時還原設定並釋放掉申請的GDI資源);
2)SelectObject 選入的使用者建立的GDI資源,需要在不再使用時選出並釋放。
3)使用的控制元件有時候會因為一些原因在Res資源中新增過圖示等GDI資源,導致在程式碼中做控制元件初始化時設定新圖示產生了GDI洩漏(從程式碼上來看是第一次設定圖示就引起了洩漏)。
3.LoadImage函式:
LoadImage函式可以載入Bitmap、Icon、Cursor三種GDI資源,需要分別使用DeleteObject、DestroyIcon、DestroyCursor來釋放,不可以混用。
LoadImage函式生成的GDI資源使用後就可以釋放,不會因為立即釋放後導致前面設定的資源不起作用。
4. CImageList儲存的GDI資源需要呼叫DeleteImageList來釋放。
5.CDC、CPEN、CBrush等MFC包裝的GDI類,在其解構函式中會呼叫DeleteObject函式取釋放資源。
6.建立GDI資源的函式和釋放GDI的函式使用次數要匹配,比如:視窗Create、OnInitDialog、以及訊息響應等函式會因為一些原因多次呼叫(比如DoModal如果被迴圈呼叫是會引起視窗的Create和OnInitDialog反覆觸發),如果在這類函式中申請GDI資源需要特別注意,因為一般作為成員變數的GDI資源的釋放在解構函式中的話就只會被呼叫一次。
7.給外部模組呼叫的函式中如果包含了GDI資源的申請需要在函式頭註釋,提醒呼叫者需要手動釋放(往往函式被包裝幾層後外層函式呼叫者很容易忽略釋放)。
8.少量程式碼是可以根據程式碼靜態檢視或者分模組除錯來找出GDI洩漏位置,但是大量程式碼排查需要藉助工具才比較有效率,這裡推薦Deleaker這款工具(GDI洩漏和記憶體洩漏都可以準確的找出程式碼行)。