Al Berger
Al Berger

Reputation: 1068

How to correct the GDI resource leakage?

I found out that after FillRgn() Windows GDI API function, the GDI object used in this function is somehow "stuck" somewhere in the internal system maps and won't delete properly: calling DeleteObject() on the object returns successfully, but the number of GDI objects for the process does not decrement. Here is the code:

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);
}

The code demonstrates that after deleting the HBRUSH handle variables 'before' and 'after' are equal; g_DC is the main window HDC.

How to delete the 'h' so that the number of GDI objects were decrementing?

Upvotes: 3

Views: 2048

Answers (2)

Adrian McCarthy
Adrian McCarthy

Reputation: 47954

Either GDI is caching the brush and region resources, or it's a bug. The count does not go down after deleting the brush or the region. Tested on Windows 7. Here's my quick-and-dirty repro code:

#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;
}

Upvotes: 1

user1764961
user1764961

Reputation: 693

When calling SelectObject() for the first time, you have to remember the return value and select it again once you are done with the DC. Also, you have to delete all created GDI objects.

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);
}

Note:
Objects retrieved by GetStockObject() can be deleted, but they don't have to.

Upvotes: 1

Related Questions