0

Now I have a button control to which I want to assign a custom window procedure,

HWND buttonControl = CreateWindow(
    L"BUTTON", 
    L"Click Me!", 
    WS_VISIBLE | WS_CHILD,
    100, 100, 200, 50,
    hWnd, //Parent Window,
    0,
    NULL,
    NULL
);

The Window Procedure :

LRESULT CALLBACK customWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    
    ...
    return DefWindowProc(hWnd, message, wParam, lParam);
}

I have found some answers that *almost answers my question, such as:


My question is :

after registering a class like,

WNDCLASS button_wc = { 0 };
button_wc.lpszClassName = L"BUTTON";
button_wc.lpfnWndProc = customWndProc; // Custom window procedure from above.

RegisterClass(&button_wc);

How am I supposed to assign it to the button control? I know that I have to use SetWindowPtrLong to assign it to the button control, but I don't understand how to do it. In the above link it is also said that I have to save the old window procedure?!

So am I supposed to call SetWindowPtrLong and assign the value when I am creating the WNDCLASS to WNDCLASS::lpfnWndProc like:

button_wc.lpfnWndProc = (WNDPROC)SetWindowPtrLong(buttonControl, GWL_WNDPROC, (LONG_PTR)&customWndProc);

or am I supposed to assign it somewhere else?


EDIT: This is how I am assigning the custom window procedure:

LRESULT customWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
    switch(message) {
        case WM_CREATE: {
            std::cout << "CREATE" << std::endl;
            break;
        }
        case WM_LBUTTONDOWN: {
            std::cout << "LBUTTONDOWN" << std::endl;
            break;
        }
        case WM_PAINT: {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
                        
            HBRUSH brush = CreateSolidBrush(RGB(20, 140, 240));
            FillRect(hdc, &ps.rcPaint, brush);

            DeleteObject(brush);

            EndPaint(hWnd, &ps);
            break;
        }
   }

   return DefSubclassProc(hWnd, message, wParam, lParam);
}

SetWindowSubclass(buttonControl, customWndProc, 17, 0); //Setting the subclass

But the window procedure is not catching the WM_CREATE and WM_LBUTTONDOWN messages but it is catching WM_PAINT messages?! Am I doing something wrong?


Any help is greatly appreciated! Thank you in advance.

  • 1
    The "BUTTON" window class already exists, so do not create it. – CGi03 Sep 02 '22 at 19:05
  • No, I want custom features on the *button* that's why I am assigning a custom *window procedure* to it. – Amrit Sanjeev Sep 02 '22 at 19:06
  • 3
    The simple answer is: You don't. See [Subclassing Controls Using ComCtl32.dll version 6](https://learn.microsoft.com/en-us/windows/win32/controls/subclassing-overview#subclassing-controls-using-comctl32dll-version-6) for instructions on how to properly subclass controls. – IInspectable Sep 02 '22 at 19:08
  • `SetWindowSubclass(/*button control*/, /*Instead of a WNDPROC a SUBWNDPROC*/, /*Any arbitrary number*/, /*But what to put here?*/)` – Amrit Sanjeev Sep 02 '22 at 19:15
  • Anything your application needs. If you don't need to access any data from within your subclass procedure, then pass a `nullptr`. – IInspectable Sep 02 '22 at 20:09
  • @IInspectable OK, but I'm still not receiving `WM_CREATE` messages – Amrit Sanjeev Sep 02 '22 at 20:10
  • Right. The value you specify there is passed on to your subclass procedure. You could allocate a structure on the heap that stores state information, for example, and pass a pointer to it as the `dwRefData`. That's just a common pattern in the Win32 API. It serves a similar purpose as the `lpParam` in the `CreateWindow` call. – IInspectable Sep 02 '22 at 20:13
  • @IInspectable Oh ok – Amrit Sanjeev Sep 02 '22 at 20:15
  • @IInspectable Ok, I'm dumb I am assigning the *custom window procedure* after the window is created, ***Obviously*** I'm not going to receive any `WM_CREATE` messages. – Amrit Sanjeev Sep 02 '22 at 20:18
  • @IInspectable Anyway, thank very much for suggesting `SetWindowSubclass`. It was way easier than whatever I was trying to do. Thank you so much. – Amrit Sanjeev Sep 02 '22 at 20:19
  • If you need your window subclass procedure to be installed prior to receiving `WM_CREATE`/`WM_NCCREATE` messages you would have to register a (local) [CBT hook](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms644977(v=vs.85)) (I *think* you can subclass a window from a `HCBT_CREATEWND` handler, though I never actually tried). – IInspectable Sep 02 '22 at 20:24
  • @IInspectable I don't think I would ever need it to be installed prior to receiving `WM_CREATE`. But it is still good to know. – Amrit Sanjeev Sep 02 '22 at 20:50
  • There's also a technique known as "super-classing", where you register a custom class (with its own class name) and then instead of calling `DefWindowProc` (or `DefSubclassProc` as you've done in your example below) you use `CallWindowProc` to call the original window procedure of the class you're augmenting. – Jonathan Potter Sep 02 '22 at 21:27

1 Answers1

0

So I'm creating a custom window procedure for a child control,

HWND buttonControl = CreateWindow(
   L"BUTTON",
   L"Click me!",
   WS_CHILD | WS_VISIBLE,
   100, 100, 200, 50,
   hWnd, // Parent Window
   0, 
   NULL,
   NULL
);

The custom window procedure :

LRESULT customWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) 
{
   switch(message) {
       case WM_LBUTTONDOWN : {
           std::cout << "Clicked" << std::endl;
           break;
       }
       case WM_LBUTTONUP : {
           std::cout << "Un-Clicked" << std::endl;
           break;
       }
   }

   return DefSubclassProc(hWnd, message, wParam, lParam); // A "DefWindowPrc" but for subclasses
}

Now I am assigning the custom window procedure to my control using SetWindowSubclass like:

SetWindowSubclass(
    buttonControl, // The window we want the subclass to be assigned to
    customWndProc, // The custom window procedure we defined above.
    17, // Any unique number to identify this subclass with
    (DWORD_PTR)nullptr // As we don't want to access data from our subclass we pass a nullptr.
)

This is all defined in commctrl.h.

And that's all I had to do to assign a custom window procedure to a child control.

Thanks to @IInspectable for the answer.