Release your DCs and take care while using Wrapper (RAII) classes

Today I was wondering the application I was working with constantly increments the GDI count. I could not identified the problem on first look because the application was hard to debug because of it’s size and use of third party release version of libraries. Anyway I tracked it down there had a small window which has some number of buttons. This window is a constantly updating one that’s why it has a lot of painting operations. To avoid the flickering, we were using a memory DC class. But the memory DC class was properly releasing all the GDI objects it possessing.  Still where’s the problem. I just cracked down the problem. The buggy code was something as follows.

// You please assume that this is the implemented ctor

CMemDC dc(GetDC(), ClientRect);

The above was the buggy code. As the documentation of GetDC says, you should release it on completion as GetDC adds a GDI handle to the application.

What’s the alternative solution?

You can use CClientDC. It’s derived from CDCpublically hence you will be able to access the all the public operations of CDC. It act as a common RAII (Resource Acquisition Is Initialization)class. where it’s locking the resource in the constructor and unlocks in the destructor. CClientDC class calls GetDC call of the passing window handle(pointer) and calls the same window’s ReleaseDC call of the same window on destruction. So that you can work without worrying about calling GetDC() and ReleaseDC() functions of the window.

You can rewrite the code as follows

CClientDC dc(this); // pass window class’s this pointer

CMemDC dc(&dc, ClientRect);

OK just wait a minute earlier I had made a call like this CMemDC(GetDC(),ClientRect); so sometime you may have a tendency to write something like this CMemDC dc(&CClientDC(this),rect); It too should work fine no? No never! Go back to C++ school again the CClientDC object creating inside the function destructs when the function call completes so the CClientDC’s destructor will free the DC object acquired in it’s construction. Also if the constructor throws some exception, it is not sure that the object destructs properly. It may skip the hidden destructor call written by compiler and will go the catch block or some other handlers. Same case is also applicable while using any RAII classes. Initialize the objects outside the function calls and pass the object for safety.

You may do some further drawing calls with the memory DC object but finally it will end up in a crash (normal while destructing the mem dc object).

// The errorneous code as follows

CMyDialog::OnPaint()
{
    CMemDC MemDC(&CClientDC(this),rect); // ERROR dont do like this.
    … // Your drawing calls goes here
}

// Correct code

CMyDialog::OnPaint()
{
    CClientDC dc(this);
    CMemDC MemDC(&dc,rect); // Correct dont do like this
    // Also the destructors will be called in the opposite way they constructed.
    // So there will not be any problems
    … // Your drawing calls goes here
} // dtors will be called here

Ok here’s the code of a simple MemDC class. Hope this very simple without any GDI any leaks. But still this is not full fledged one. You can go to codeproject or codeguru to get some well implemented advanced memory DC classes.

class CMemDC : public CDC
{
    CBitmap m_Bitmap;
    CRect m_Rect;
    CDC* m_pDC;
public:

    /* Stores the dc to blit to screen, store the rectangle area for size */
    CMemDC(CDC* pDC, CRect rect) : m_pDC(pDC), m_Rect(rect)
    {
        CreateCompatibleDC(pDC); // Create DC for off-screen drawing
        // Further drawing will be happened in bitmap instead of screen
        m_Bitmap.CreateCompatibleBitmap( pDC, rect.Width(),rect.Height());
    }
    ~CMemDC()
    {
        // Finally blt to screen
        m_pDC->BitBlt( 0, 0, m_Rect.Width(), m_Rect.Height(), this, 0,0,SRCCOPY);
    }
};

I conclude as follows

1. If you have called GetDC(), it should be released properly

2. Don’t construct RAII objects within function calls because it will be destroyed after function calls. Also if some exception occured, it will not be release properly.

Leave a Reply

 

 

 

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

blog comments powered by Disqus