I use MinGW32 with Code::Blocks IDE on Windows 10. I'm trying to assign an image list to a button via a "BUTTON_IMAGELIST", but the "SendMessage" function always fails (returns '0'). However, "GetLastError()" doesn't give me an error either. Assigning a single image to the button works, but with an image list the button stays blank. What am I doing wrong here?
main.cpp
#include <iostream>
#include <windows.h>
#include <commctrl.h>
#include "resource.h"
HIMAGELIST ImageList;
LRESULT CALLBACK WindowProcedure ( HWND WindowHandle, UINT message, WPARAM wParam, LPARAM lParam )
{
switch ( message )
{
case WM_CREATE:
{
HWND ButtonOne = CreateWindowEx( 0, "BUTTON", "", BS_PUSHBUTTON | BS_BITMAP | WS_CHILD | WS_VISIBLE, 40, 10, 50, 50, WindowHandle, (HMENU)( IDC_BUTTON_ONE ), GetModuleHandle( nullptr ), nullptr );
HANDLE Image = LoadImage( GetModuleHandle( NULL ), MAKEINTRESOURCE( IDB_BITMAP_SQUARE ), IMAGE_BITMAP, 50, 50, LR_DEFAULTCOLOR );
ImageList = ImageList_Create( 50, 50, ILC_COLORDDB, 1, 0 );
ImageList_Add( ImageList, (HBITMAP)Image, nullptr );
RECT rect = { 0, 0, 50, 50 };
BUTTON_IMAGELIST *ButtonImageList = new BUTTON_IMAGELIST;
ButtonImageList->himl = ImageList;
ButtonImageList->margin = rect;
ButtonImageList->uAlign = BUTTON_IMAGELIST_ALIGN_CENTER;
std::cout << SendMessage( ButtonOne, BCM_SETIMAGELIST, (WPARAM)0, (LPARAM)( ButtonImageList ) ); //output = 0
}
break;
case WM_DESTROY:
ImageList_Destroy( ImageList );
PostQuitMessage ( 0 );
break;
default:
return DefWindowProc ( WindowHandle, message, wParam, lParam );
}
return 0;
}
const std::string WindowClassName = "CommonControlsTest";
int WINAPI WinMain ( HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow )
{
INITCOMMONCONTROLSEX *ComCon;
ComCon->dwSize = sizeof( INITCOMMONCONTROLSEX );
ComCon->dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx( ComCon );
HWND WindowHandle;
MSG Messages;
WNDCLASSEX WindowClass;
WindowClass.hInstance = hThisInstance;
WindowClass.lpszClassName = WindowClassName.c_str();
WindowClass.lpfnWndProc = WindowProcedure;
WindowClass.style = CS_DBLCLKS;
WindowClass.cbSize = sizeof ( WNDCLASSEX );
WindowClass.hIcon = LoadIcon ( NULL, IDI_APPLICATION );
WindowClass.hIconSm = LoadIcon ( NULL, IDI_APPLICATION );
WindowClass.hCursor = LoadCursor ( NULL, IDC_ARROW );
WindowClass.lpszMenuName = NULL;
WindowClass.cbClsExtra = 0;
WindowClass.cbWndExtra = 0;
WindowClass.hbrBackground = reinterpret_cast<HBRUSH>( COLOR_BACKGROUND );
if ( !RegisterClassEx ( &WindowClass ) )
return 0;
WindowHandle = CreateWindowEx ( 0, WindowClassName.c_str(), "Common Controls Test", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 500, 300, HWND_DESKTOP, NULL, hThisInstance, NULL );
ShowWindow ( WindowHandle, nCmdShow );
UpdateWindow( WindowHandle );
while ( ( GetMessage ( &Messages, NULL, 0, 0 ) ) )
{
TranslateMessage( &Messages );
DispatchMessage( &Messages );
}
return Messages.wParam;
}
resource.rc
#include "Resource.h"
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "CommonControlsTest.exe.manifest"
IDB_BITMAP_SQUARE BITMAP "Square.bmp"
resource.h
#define IDC_BUTTON_ONE 101
#define IDB_BITMAP_SQUARE 201
CommonControlsTest.exe.manifest
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="*"
name="CompanyName.ProductName.YourApplication"
type="win32"
/>
<description>Your application description here.</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>
Your resource.h
file needs to define the CREATEPROCESS_MANIFEST_RESOURCE_ID
and RT_MANIFEST
constants as 1 and 24, respectively.
resource.h
#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1
#define RT_MANIFEST 24
#define IDC_BUTTON_ONE 101
#define IDB_BITMAP_SQUARE 201
Also, you do not need to allocate the BUTTON_IMAGELIST
dynamically (you are leaking it):
RECT rect = { 0, 0, 50, 50 };
BUTTON_IMAGELIST Button ImageList {};
ButtonImageList.himl = ImageList;
ButtonImageList.margin = rect;
ButtonImageList.uAlign = BUTTON_IMAGELIST_ALIGN_CENTER;
std::cout << SendMessage( ButtonOne, BCM_SETIMAGELIST, 0, (LPARAM) &ButtonImageList );
Same with INITCOMMONCONTROLSEX
(which you are not even allocating):
INITCOMMONCONTROLSEX ComCon {};
ComCon.dwSize = sizeof( ComCon );
ComCon.dwICC = ICC_LISTVIEW_CLASSES;
InitCommonControlsEx( &ComCon );
When an API asks for a pointer, that doesn't always mean dynamic memory should be used.