如何纠正GDI资源泄漏?

时间:2013-08-26 22:59:09

标签: c++ windows winapi gdi

我发现在FillRgn()Windows GDI API函数之后,此函数中使用的GDI对象以某种方式“卡在”内部系统映射中的某处并且不会正确删除:在对象上调用DeleteObject()成功返回,但进程的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后的句柄变量'之前'和'之后'相等; g_DC是HDC的主窗口。

如何删除'h'以便GDI对象的数量减少?

2 个答案:

答案 0 :(得分: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()检索的对象,但它们不必删除。

答案 1 :(得分: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;
}