c++winapicreatewindowexregisterclass

window fails to initiate when specifying a custom class name registered with RegisterClass to CreateWindowEx but only the second time I do it


I'm creating a window that's supposed to contain buttons. When I create the first window (the container) I use a WNDCLASS to specify a WindowProc callback function to lpfnWndProc and it works as intended. When I do it the second time with a window, specifying a callback used to detect when the button is clicked, it doesn't work; the CreateWindowEx call there returns NULL (comment below shows where).

When I don't create WNDCLAS wB below and specify some other valid lpClassName to CreateWindowEx instead, such as Button it works fine.

code used

#pragma comment(linker, "/entry:wWinMainCRTStartup")

#include <windows.h>

#include <iostream>

HWND button;

LRESULT CALLBACK ButtonWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
                                  LPARAM lParam);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine,
                    int nCmdShow) {
  WNDCLASS wc = {};

  wc.lpfnWndProc = WindowProc;
  wc.hInstance = hInstance;
  wc.lpszClassName = L"textedit";

  RegisterClass(&wc);

  HWND hwnd = CreateWindowEx(
      0, wc.lpszClassName, L"textedit", WS_OVERLAPPEDWINDOW,

      CW_USEDEFAULT, CW_USEDEFAULT, 340, 240, NULL, NULL, hInstance, NULL);
  if (hwnd == NULL) {
    return 0;
  }

  HWND hWndEdit = CreateWindowEx(WS_EX_CONTROLPARENT, L"Edit", L"id here",
                                 WS_BORDER | WS_CHILD | WS_VISIBLE, 94, 20, 140,
                                 40, hwnd, NULL, NULL, NULL);
 
  // if I leave wcB unused/remove it and
  // specify L"Button" as the lpClassName to CreateWindowEx below, the button displays

  WNDCLASS wcB = {};

  wcB.lpfnWndProc = ButtonWindowProc;
  wcB.hInstance = hInstance;
  wcB.lpszClassName = L"modify";

  RegisterClass(&wcB);

  ShowWindow(hwnd, nCmdShow);
  // doing the exact same thing I did when creating the first window
  // but it fails to create the window

  HWND hButton =
      CreateWindowEx(0L, L"modify", L"modify", BS_FLAT | WS_VISIBLE | WS_CHILD,
                      94, 70, 140, 50, hwnd, NULL, hInstance, NULL);
  ShowWindow(hButton, nCmdShow);

  // here hButton is NULL     <----------------------

  if (hButton == NULL) {
    return 0;
  }
  MSG msg = {};
  while (GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return 0;
}

LRESULT CALLBACK ButtonWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
                                  LPARAM lParam) {
  switch (uMsg) {
    case WM_DESTROY:
      PostQuitMessage(0);
      return 0;

    case BN_CLICKED:
         std::cout << "clicked";
    case WM_PAINT:
      PAINTSTRUCT ps;
      HDC hdc = BeginPaint(hwnd, &ps);
      FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
      EndPaint(hwnd, &ps);

      return 0;
  }

  return 0;
  return DefWindowProc(hwnd, uMsg, wParam, lParam);
};

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
                            LPARAM lParam) {
  switch (uMsg) {
    case WM_DESTROY:
      PostQuitMessage(0);
      return 0;

    case WM_PAINT:
      PAINTSTRUCT ps;
      HDC hdc = BeginPaint(hwnd, &ps);
      FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
      EndPaint(hwnd, &ps);

      return 0;
  }

  return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

GetLastError() returns 1400, but I don't understand how the handle hwnd could possible be invalid because as I mentioned before, when I just change the lpClassName I pass to CreateWindowEx, it works without errors.

I would appreciate any help in finding out why the window (the HWND of which is hButton) does not initiate, and possible workarounds.


Solution

  • The solution to the problem in the title was solved by the comment by Igor Tandetnik:

    ButtonWindowProc returns 0 for all messages it doesn't handle, and doesn't pass them to DefWindowProc. In particular, it returns 0 in response to WM_NCCREATE, which is a signal that the window creation has failed. You probably didn't mean to put return 0; right before return DefWindowProc(...);

    After fixing that, the window was created but was invisible. To fix that I used FillRect with a valid brush handle

     SetDCBrushColor(hdc, bkcolor);
     SetBkColor(hdc, RGB(187, 189, 189));
     FillRect(hdc, &ps.rcPaint, (HBRUSH)GetStockObject(DC_BRUSH));
    

    To detect left mouse button clicks you need to check for the WM_LBUTTONDOWN message.