I am creating a Win32 ComboBox for the first time. And I have a problem here.
When calling CreateWindow
for the ComboBox, it calls the WndProc callback function again with the WM_CREATE
message, so what happens is the ComboBox makes a child ComboBox, again and again like recursion.
Here is the code:
#include <stdio.h>
#include <conio.h>
#include <Windows.h>
#include <random>
#include <time.h>
#include <string>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hInst;
LPCTSTR lpszClass = L"ComboBox";
const WCHAR *items[] = { L"Apple", L"Orange", L"Melon", L"Grape", L"Strawberry" };
HWND hwnd;
enum COMMAND_ID {
COMMAND_ID_CONTROL_COMBO_0
};
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
{
srand(time(NULL));
g_hInst = hInstance;
WNDCLASS wndClass;
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hInstance = hInstance;
wndClass.lpfnWndProc = WndProc;
wndClass.lpszClassName = lpszClass;
wndClass.lpszMenuName = NULL;
wndClass.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&wndClass);
hwnd = CreateWindow(
lpszClass,
lpszClass,
WS_CAPTION | WS_SYSMENU | WS_THICKFRAME,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
(HMENU)NULL,
hInstance,
NULL);
ShowWindow(hwnd, nCmdShow);
MSG msg;
while (true)
{
GetMessage(&msg, NULL, 0, 0);
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
static HWND hCombo;
static WCHAR str[128];
switch (msg)
{
case WM_CREATE:
{
hCombo = CreateWindow(
L"combobox",
NULL,
WS_CHILD | WS_VISIBLE | WS_BORDER | CBS_DROPDOWN,
10, 10, 200, 200,
hWnd,
(HMENU)COMMAND_ID_CONTROL_COMBO_0,
g_hInst,
NULL);
for (int i = 0; i < 5; ++i)
{
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)items[i]);
}
SendMessage(hCombo, CB_SETCURSEL, 0, NULL);
}
break;
case WM_COMMAND:
{
switch (LOWORD(wparam))
{
case COMMAND_ID_CONTROL_COMBO_0:
switch (HIWORD(wparam))
{
case CBN_SELCHANGE:
{
int iCurSel = SendMessage(hCombo, CB_GETCURSEL, NULL, NULL);
SendMessage(hCombo, CB_GETLBTEXT, iCurSel, (LPARAM)str);
SetWindowText(hWnd, str);
}
break;
case CBN_EDITCHANGE:
GetWindowText(hCombo, str, 128);
SetWindowText(hWnd, str);
break;
}
break;
default:
break;
}
}
return 0;
}
return DefWindowProc(hWnd, msg, wparam, lparam);
}
And here is the result:
I tried to put some boolean flag to execute WM_CREATE
only once, and it works, I mean only creating one ComboBox without any child in it.
But, it just looked like only a white window with a border mark, there's no arrow button or anything to dropdown page that the ComboBox is supposed to have.
This recursive case never happened when I was creating different controls like Buttons, CheckBoxes, ListBoxes, etc.
And the ComboBox created doesn't look like it has the proper shape, too.
Hope I am just missing something simple.
WM_CREATE
message is sent to the window procedure of the new window after the window is created. Your firstWM_CREATE
message is generated by this linehwnd = CreateWindow()
. Then you create another window in firstWM_CREATE
message, so it will generate secondWM_CREATE
message. Because you use the same registered class ("ComboBox"
/"combobox"
, it is not case sensitive) to create all these windows, all of them use the same one window procedure. So you receiveWM_CREATE
message again and again untilCreateWindow
fail to create a window and returnNULL
.The root cause is you register a class with the same name as the existing system class:
"ComboBox"
/"combobox"
. This new registered class override the existing one. It is just a common window instead of a predefined Combobox control as @RemyLebeau pointed out.Refer to "How the System Locates a Window Class".
To make the Combobox display in expected shape, what you need to do is changing the
lpszClass
to a non-predefined one, for example, like"SimpleComboBoxExample"
.It is suggested to use predefined macro of Combobox Class Name:
WC_COMBOBOX
instead ofL"combobox"
.More reference: "How to Create a Simple Combo Box".