Drawing Bitmap Resources in ATL 7.0 Controls
This article provides minimal and easy to understand code samples for loading, drawing and releasing bitmap resources in ATL 7.0 controls.
Tags: activex com cpp programming
Introduction
The code samples in the following paragraphs assume that there exists a custom ATL 7.0 COM control called CMyControl, which is derrived from CComControl. Such a control can be created using the ATL Control wizzard in Visual Studio or - for advanced programmers - by manually writing the corresponding code.
All Windows GDI functions used below are declared in Wingdi.h, which is automatically included with Windows.h:
#include <windows.h>
It is also assumed that the ATL project already contains a bitmap resource holding the image to be drawn. When adding bitmaps or other resources to a project, Visual Studio will automatically create an internal resource identifier in the project's resource.h header file. In the following samples, this identifier is assumed to be IDB_MYBITMAP.
Loading The Bitmap
Before the bitmap resource can be drawn, it needs to be loaded into memory. This is accomplished by the LoadBitmap function, which requires a handle to the module instance running the control. This handle can be retrieved using the GetModuleInstance method of the _AtlBaseModule object, which is automatically created in any ATL 7.0 project, and which replaces the CComModule class used in earlier versions of ATL.
The second parameter of the LoadBitmap function is the name of the bitmap resource which, in the scope of this article, is the resource identifier discussed in the previous paragraph. The MAKEINTRESOURCE macro converts the identifier into a string:
CMyControl::CMyControl ()
{
myBitmap = LoadBitmap(_AtlBaseModule.GetModuleInstance(),
MAKEINTRESOURCE(IDB_MYBITMAP));
}
The newer versions of the Windows GDI also provide a new function, LoadImage, which replaces the now deprecated LoadBitmap function:
CMyControl::CMyControl ()
{
myBitmap = (HBITMAP)LoadImage(_AtlBaseModule.GetModuleInstance(),
MAKEINTRESOURCE(IDB_MYBITMAP), IMAGE_BITMAP,
0, 0, LR_DEFAULTCOLOR);
}
Drawing The Bitmap
Drawing the bitmap on the control's surface can be accomplished by overriding the control's OnDraw or OnDrawAdvanced method and consists of several simple steps. First a compatible device context needs to be created, which only exists in memory and holds the parameters for painting and drawing operations. The bitmap is then selected as the active object into the memory device context, and the original default bitmap is backed up, so it can be restored after the drawing is finished:
HRESULT CMyControl::OnDrawAdvanced (ATL_DRAWINFO& di)
{
if (myBitmap != NULL)
{
// create memory device context
HDC hmdc = CreateCompatibleDC(di.hdcDraw);
// select bitmap object into device context
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hmdc, myBitmap);
if (hOldBitmap != NULL)
{
// get bitmap dimensions
BITMAP bi;
GetObject(myBitmap, sizeof(bi), &bi);
// draw entire bitmap at position x=3 and y=5
BitBlt(di.hdcDraw, 3, 5, bi.bmWidth, bi.bmHeight,
hmdc, 0, 0, SRCCOPY);
// restore original default object
SelectObject(di.hdcDraw, hOldBitmap);
}
// release device context
DeleteDC(hmdc);
}
}
In this example, the actual drawing is performed by the BitBlt function, which copies the pixels from the bitmap into the specified location of the drawing surface, but other drawing functions, such as MaskBlt or StretchBlt might be used as well.
Releasing The Bitmap
Once the bitmap is no longer needed, the system resources associated with it should be released by the control. In the current scenario, the code for releasing the bitmap can conveniently be put into the control's destructor:
CMyControl::~CMyControl ()
{
if (myBitmap != NULL)
{
DeleteObject(myBitmap);
}
}
Bitmap objects (and any other brush or pen objects for that matter) should not be released while they are still selected into a device context.