In this tip, I present a solution for putting a new image on an existing toolbar button.
I've searched the web, but I haven't found a satisfying recipe for putting a new image on an existing toolbar button - even the Microsoft documentation was of limited help.
All of you who are facing a similar problem, I would like to present my solution here.
Attempt 1: There is the
TB_CHANGEBITMAP message available, but before using it - the image list of the toolbar must be adjusted with
ImageList_Add(). This may cause a realloc of the image list in case its capacity is exhausted and some sources say you may have to rebind the image list to the toolbar. I did not manage to get this to work, the
TB_CHANGEBITMAP message always returned
Attempt 2: To work around the
TB_CHANGEBITMAP message, I tried the
TB_SETBUTTONINFO messages. These messages return
TRUE, but after the calls, the old image was gone and the toolbar button only showed the background color.
Attempt 3: Only after that, I had the idea to simply exchange the image in the image list of the toolbar, whereby the image index does not change, the image list size does not change and no changes have to be made to the toolbar button.
A first positive side effect of this approach is that the image list does not increase in size even when the image is swapped several times.
ImageList_Add() always creates its own copies of the color bitmap and mask bitmap for the image list. On the one hand, this keeps the responsibility for the original
HBITMAP handles in the application and does not pass them to the windows manager, making it easier to keep the application code clean. On the other hand, it is no longer possible to find an earlier bitmap after the
ImageList_Add() call with the help of the original
HBITMAP handles (e.g., to swap the image back).
The second positive side effect of this approach is that it is not necessary to find previous bitmaps at all.
Using the Code
The following method swaps the bitmaps of a toolbar button in the toolbar's image list and deletes the bitmaps previously registered for the button.
int ReplaceButtonImage(UINT uiCommandID, HBITMAP hbmpDDBImage,
tbi.cbSize = sizeof(TBBUTTONINFO);
tbi.dwMask = TBIF_IMAGE | TBIF_COMMAND;
if (::SendMessage((HWND)_hResObj, TB_GETBUTTONINFO,
(WPARAM)uiCommandID, (LPARAM)&tbi) < 0)
int iImageCount = ::ImageList_GetImageCount(_hEnabledImageList);
if (tbi.iImage < 0 || tbi.iImage >= iImageCount)
if (hbmpDDBImage == NULL)
HBITMAP hbmpDDBImageOld = NULL;
HBITMAP hbmpDDBMaskOld = NULL;
if (::ImageList_GetImageInfo(_hEnabledImageList, tbi.iImage, &ii) == TRUE)
hbmpDDBImageOld = ii.hbmImage;
hbmpDDBMaskOld = ii.hbmMask;
if (::ImageList_Replace(_hEnabledImageList, tbi.iImage,
hbmpDDBImage, hbmpDDBMask) == TRUE)
if (hbmpDDBImageOld != NULL)
if (hbmpDDBMaskOld != NULL)
Points of Interest
In the end, the simplest solution turned out to be the best solution.
- 22nd February, 2021: Initial version