2013-08-26 85 views
3

我發現,在FillRgn()Windows GDI API函數之後,此函數中使用的GDI對象以某種方式「卡住」在內部系統映射的某個位置,並且不會被正確刪除:調用對象的DeleteObject()成功返回,但該進程的GDI對象數量不會減少。代碼如下:如何糾正GDI資源泄漏?

void gditest() 
{ 
    HBRUSH h = CreateSolidBrush(RGB(255, 237, 5)); 
    HRGN rgn = CreateRectRgn(0, 100, 100, 0); 
    FillRgn(g_DC, rgn, h); 

    int before = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS); 
    SelectObject(g_DC, GetStockObject(WHITE_BRUSH)); 
    int rs = DeleteObject(h); 
    if (!rs) 
     throw; 
    int after = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS); 
} 

該代碼演示了刪除HBRUSH句柄變量'before'和'after'後相等; g_DC是HDC的主窗口。

如何刪除'h',使GDI對象的數量遞減?

+0

您正在泄漏該區域句柄。這可能是你正在觀察的泄漏?你用'h'做的事對我來說看起來不錯;實際上,'SelectObject'調用是多餘的,因爲你從未真正將你的畫筆選入'g_DC'。 –

+0

我忘記提及考慮的資源是HBRUSH。是的,該區域不會被刪除,但是測試代碼的重點在於,刪除HBRUSH後,GDI對象的數量不會減少,因爲我認爲它應該。 –

+3

正如[本文](http://msdn.microsoft.com/en-us/magazine/cc301756.aspx)中所述,GDI緩存純色畫筆。你看到的是刷子被邏輯刪除,但仍然物理存在於緩存中。 –

回答

2

第一次調用SelectObject()時,必須記住返回值,並在完成DC後再次選擇它。另外,您必須刪除所有創建的GDI對象。

void gditest() 
{ 
    HBRUSH h = CreateSolidBrush(RGB(255, 237, 5)); 
    HRGN rgn = CreateRectRgn(0, 100, 100, 0); 
    FillRgn(g_DC, rgn, h); 

    int before = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS); 
    HBRUSH oldBrush = SelectObject(g_DC, GetStockObject(WHITE_BRUSH)); 
    SelectObject(g_DC, oldBrush); 
    int rs = DeleteObject(h); 
    if (!rs) 
     throw; 
    DeleteObject(rgn); 
    int after = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS); 
} 

注:由GetStockObject檢索
對象()可以刪除,但他們沒有。

+0

正如上面所提到的,SelectObject()調用是多餘的,並且放在這裏只是爲了確保在g_DC中未選擇HBRUSH'h',如果它是選在FillRgn()的內部某處。 –

+0

我把它放在那裏只是爲了強調我的觀點。無論如何,我會建議爲GDI對象編寫RAII包裝器。這樣你遲早會用一隻腳開槍自己。 (顯然你已經做過) – user1764961

0

GDI緩存刷子和區域資源,或者它是一個錯誤。刪除畫筆或區域後,計數不會減少。在Windows 7上測試過。這裏是我的快速重複代碼:

#include <cassert> 
#include <iostream> 
#include <windows.h> 

void PrintGdiCount() { 
    std::cout << ::GetGuiResources(::GetCurrentProcess(), GR_GDIOBJECTS) 
      << std::endl; 
} 

int main() { 
    PrintGdiCount(); 
    ::GdiSetBatchLimit(1); // disable batching 
    HDC hdcScreen = ::GetDC(NULL); 
    PrintGdiCount(); 
    HDC hdcMemory = ::CreateCompatibleDC(hdcScreen); 
    PrintGdiCount(); 
    HBITMAP hbmpMemory = ::CreateCompatibleBitmap(hdcScreen, 100, 100); 
    PrintGdiCount(); 
    HBITMAP hbmpOld = reinterpret_cast<HBITMAP>(::SelectObject(hdcMemory, hbmpMemory)); 
    PrintGdiCount(); 
    HBRUSH hbrush = ::CreateSolidBrush(RGB(255, 127, 32)); 
    PrintGdiCount(); 
    HRGN hrgn = ::CreateRectRgn(0, 0, 50, 50); 
    PrintGdiCount(); 
// ::FillRgn(hdcMemory, hrgn, hbrush); // doesn't affect GDI count 
    PrintGdiCount(); 
    BOOL bDeletedBrush = ::DeleteObject(hbrush); 
    assert(bDeletedBrush); 
    PrintGdiCount(); // expected decrement, but it doesn't 
    ::DeleteObject(hrgn); 
    PrintGdiCount(); // expected decrement, but it doesn't 
    ::SelectObject(hdcMemory, hbmpOld); 
    ::DeleteObject(hbmpMemory); 
    PrintGdiCount(); 
    ::DeleteDC(hdcMemory); 
    PrintGdiCount(); 
    ::ReleaseDC(NULL, hdcScreen); 
    PrintGdiCount(); // result is 2 higher than initial count 
    return 0; 
} 
+0

嘗試調用'GdiFlush()',看看它是否有所作爲。如果操作系統正在批量填充,那麼必須緩存刷子。 –

+0

@Jonathan Potter:我在開始時禁用了'GdiSetBatchLimit(1)'批處理,所以'GdiFlush()'應該是不相關的。 –