The procedure of control subclassed does not work. Why? - c++

I recently tried to subclass a control using the SetWindowSubclass() function. And to my surprise, it worked smoothly at first. I created the control procedure, but it doesn't receive messages like WM_LBUTTONDOWN or WM_KEYDOWN, and I don't understand why! I searched all my time yesterday and I confess that I am losing hope.
Here is my code, simplified (very long in normal times!):
HWND button = CreateWindow("BUTTON", NULL, WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, 100, 200, 250, 50, hwnd, (HMENU)ID_B_JOUER, instance, NULL);
SetWindowSubclass(boutton, ControlProc, ID_B_JOUER, 0);
And here is the control procedure:
LRESULT CALLBACK ControlProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch(msg)
{
case WM_...:
{
MessageBox(hwnd, "Bonjour!", "Message", MB_ICONINFORMATION); // Here it does not work!!! Something be the message.
break;
}
default:
{
DefSubclassProc(hwnd, msg, wParam, lParam);
}
}
return 0;
}
I want to clarify that I am using the GetLastError() function and it returned 0 (no problem). I hope someone has a solution because it's very weird.

You are creating a new BUTTON control and assigning it to a variable named button, but you are subclassing using a different variable named boutton instead.
Assuming that is just a typo, your ControlProc() is returning 0 for every message, it is ignoring the return value of DefSubclassProc(), which may not be 0. You MUST return what DefSubclassProc() returns for unhandled messages.
HWND button = CreateWindow("BUTTON", NULL, WS_VISIBLE | WS_CHILD | BS_OWNERDRAW, 100, 200, 250, 50, hwnd, (HMENU)ID_B_JOUER, instance, NULL);
SetWindowSubclass(button, &ControlProc, ID_B_JOUER, 0);
...
LRESULT CALLBACK ControlProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (msg)
{
case WM_LBUTTONDOWN:
case WM_KEYDOWN:
{
MessageBox(hwnd, "Bonjour!", "Message", MB_ICONINFORMATION);
break;
}
default:
{
return DefSubclassProc(hwnd, msg, wParam, lParam);
}
}
return 0;
}

Related

How to use SetWindowPtrLong to set a custom window procedure to a child control?

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:
How to change a window procedure at runtime?
Why you have to save the original window procedure of the window.
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.
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.

Create event handler at runtime without using WndProc win32 c++

While using C#, it was easy before to create an event handler at runtime like:
Button button1 = new button1();
button1.click += Button_Click(); //Create handler
button1.click -= Button_Click(); //Remove handler
public void Button_Click()
{
//Button clicked
}
But in win32 I am stuck with WndProc callback where I have to handle all events. I want to create a handler for specific message and attach it to specific void.
Currently, I am using WndProc to catch WM_CREATE message and draw controls:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
Draw(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
void Draw(HWND hWnd)
{
HWND button4 = CreateWindow(L"button", L"button4", WS_CHILD | WS_VISIBLE , 329, 118, 112, 67, hWnd, (HMENU)1001, hInst, NULL);
HWND button3 = CreateWindow(L"button", L"button3", WS_CHILD | WS_VISIBLE , 212, 118, 112, 67,
...
}
But I want to create or remove event handler instead of using WndProc at runtime something like:
AddHandler WM_CREATE , Draw(hWnd);
DelHandler WM_CREATE , Draw(hWnd);
What I have tried ?
The problem with SetWindowsHookEx is that its handles entire messages like WndProc. I don't want an handler that handles entire window messages and skip some of them. May be this can create performance or memory leak issue's.
Edit: Implemented example from answer:
#include <unordered_map>
using msgHandler = LRESULT(*)(HWND, UINT, WPARAM, LPARAM);
std::unordered_map<UINT, msgHandler> messageHandlers;
LRESULT handleCreate(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
//Draw some buttons to see whether event WM_CREATE called or not
HWND button4 = CreateWindow(L"button", L"button4", WS_CHILD | WS_VISIBLE, 329, 118, 112, 67, hWnd, (HMENU)1001, hInst, NULL);
HWND button3 = CreateWindow(L"button", L"button3", WS_CHILD | WS_VISIBLE, 212, 118, 112, 67, hWnd, (HMENU)1002, hInst, NULL);
HWND button2 = CreateWindow(L"button", L"button2", WS_CHILD | WS_VISIBLE, 329, 46, 112, 67, hWnd, (HMENU)1003, hInst, NULL);
HWND button1 = CreateWindow(L"button", L"button1", WS_CHILD | WS_VISIBLE, 212, 46, 112, 67, hWnd, (HMENU)1004, hInst, NULL);
return 0;
}
LRESULT handleClose(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
//Quit form
PostQuitMessage(0);
return 0;
}
void AddHandler()
{
messageHandlers[WM_CREATE] = handleCreate;
messageHandlers[WM_DESTROY] = handleClose;
}
void DelHandler()
{
messageHandlers.erase(WM_CREATE);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
auto handler = messageHandlers.find(msg);
if (handler != messageHandlers.end()) return handler->second(hWnd, msg, wParam, lParam);
return DefWindowProc(hWnd, msg, wParam, lParam);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow){
AddHandler();
//DelHandler();
...
The message ID is just an unsigned integer, so there's not really anything special about it. Although the giant switch statement is one common way to handle messages, you can do things quite differently if you choose. To support dynamic insertion/deletion of handlers, one possibility would be to use an std::unordered_map:
// a message handler receives the normal parameters:
using msgHandler = LRESULT(*)(HWND, UINT, WPARAM, LPARAM);
// a map from message numbers to the handler functions:
std::unordered_map<UINT, msgHandler> messageHandlers;
// A couple of message handler functions:
LRESULT handleCreate(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
// ...
}
LRESULT handleDraw(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
// ...
}
// register them to handle the appropriate messages:
messageHandlers[WM_CREATE] = handleCreate;
messageHandlers[WM_PAINT] = handleDraw;
// and then our (now really tiny) window proc that uses those:
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
auto handler = messageHandlers.find(msg);
if (handler != messageHandlers.end())
return handler->second(hWnd, msg, wParam, lParam);
return DefWindowProc(hWnd, msg, wParam, lParam);
}
Since we're just storing pointers to functions in an std::unordered_map, adding, finding, or deleting a handler all just use the normal operations for adding, finding, or deleting something in an std::unodered_map (e.g., messageHandlers.erase(WM_CREATE); to erase the WM_CREATE handler from the map).
If you want to get more elaborate with this, you can create specific types for handling different messages, so (for example) one that doesn't receive anything meaningful in its lParam simply won't receive an lParam at all, while another that receives two things "smooshed" together, one in the low word of lParam, and the other in the high word of lParam can receive them broken apart into two separate parameters. But that's a lot more work.
You might also want to look for WindowsX.h, a header Microsoft provides (or at least used to provide) in the SDK that handles mapping a little like I've outlined above (the latter version, where each handler receives parameters that represent the logical data it receives, instead of the WPARAM and LPARAM used to encode that data.
You could process messages dynamically like this:
typedef void (*FHANDLE)();
std::vector<FHANDLE> handles;
void AddHandler(FHANDLE handle)
{
handles.push_back(handle);
}
void DelHandler(FHANDLE handle)
{
auto it = std::find(handles.begin(), handles.end(), handle);
handles.erase(it);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
for (int i = 0; i < handles.size(); i++)
{
handles[i]();
}
break;
...
}
}
And add/del the handle:
void myclick1()
{
MessageBox(0, L"test1", L"message", 0);
}
void myclick2()
{
MessageBox(0, L"test2", L"message", 0);
}
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
AddHandler(myclick1);
AddHandler(myclick2);
//DelHandler(myclick2);
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
...
}

Change WndProc of the window

I try to change standart WndProc function. I have this code:
HWND btn = CreateWindowEx(WS_EX_TRANSPARENT | WS_EX_CLIENTEDGE, L"BUTTON", L"Window title", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON
, 50, 50, 50, 50, (HWND)XApplicationMainWindow->window->_wnd, (HMENU)123,
(HINSTANCE)GetWindowLongPtr(XApplicationMainWindow->window->_wnd, GWLP_HINSTANCE), NULL);
SetWindowLongPtrW(btn, GWLP_WNDPROC, (LONG_PTR)SubclassWindowProc);
I can use L"BUTTON" class name, but when I change WndProc function I'll have a problem.
On this picture, you can see the blank square and normal button. If I try to create new WNDCLASS or WNDCLASSEX, I'll have nothing... Why?
How can I change the standart WndProc function, if I use L"BUTTON" class name?
It's my second WndProc:
LRESULT CALLBACK SubclassWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CREATE:
break;
case WM_COMMAND:
//Event click
switch (LOWORD(wParam))
{
case 123:
OutputDebugStringA("Subclass click2");
break;
default:
break;
}
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
DefWindowProc() is the wrong window procedure for your SubclassWindowProc() to be calling.
You need to call the previous window procedure that you are replacing - the window procedure that handles all of the button's default behaviors (like drawing the button so it actually looks like a button, and responding to user input like a button, etc). SetWindowLongPtr() returns a pointer to that procedure to you, but you are currently ignoring it.
See Subclassing Controls on MSDN for more details.
Try this instead:
WNDPROC btnWndProc;
LRESULT CALLBACK SubclassWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_COMMAND:
//Event click
switch (LOWORD(wParam))
{
case 123:
OutputDebugStringA("Subclass click2");
break;
}
break;
}
return CallWindowProc(hWnd, btnWndProc, uMsg, wParam, lParam);
}
...
HWND btn = CreateWindowEx(...);
btnWndProc = (WNDPROC) SetWindowLongPtrW(btn, GWLP_WNDPROC, (LONG_PTR)SubclassWindowProc);
Alternatively, using SetWindowSubclass(), which is safer than using SetWindowsLongPtr(), eg:
LRESULT CALLBACK SubclassWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) {
switch (uMsg) {
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, SubclassWindowProc, uIdSubclass);
break;
case WM_COMMAND:
//Event click
switch (LOWORD(wParam))
{
case 123:
OutputDebugStringA("Subclass click2");
break;
}
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
...
HWND btn = CreateWindowEx(...);
SetWindowSubclass(btn, SubclassWindowProc, 1, 0);
Now, that being said, your subclass will never call OutputDebugStringA(), because it will never receive the WM_COMMAND message you are expecting. When a button is clicked, a WM_COMMAND message is not sent to the button itself. The button posts a WM_COMMAND message to the button's parent window instead (in this case, to XApplicationMainWindow->window->_wnd). So, you need to handle the WM_COMMAND message in the window procedure of the parent window, not in the window procedure of the button itself.
Otherwise, if you still want to subclass the button itself, you will have to handle the WM_LBUTTON(DOWN|UP) and WM_KEY(DOWN|UP)/WM_CHAR messages that the button receives and then subsequently translates into a WM_COMMAND message for its parent window.

WM_COMMAND not received in subclassed WinAPI button

I have a basic wrapper class for my buttons created in C++ with WinAPI. I try to handle messages but it looks like not everything reaches my WndProc
class MyButton {
public:
MyButton(HINSTANCE, HWND);
private:
HWND _hWnd;
static LRESULT CALLBACK _WndProc(HWND, UINT, WPARAM, LPARAM, UINT_PTR, DWORD_PTR);
};
MyButton::MyButton(HINSTANCE hInst, HWND hParent)
{
this->_hWnd = CreateWindow(
TEXT("BUTTON"),
TEXT("CLICK ME"),
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
10, 10,
100, 25,
hParent,
NULL,
hInst,
this
);
SetWindowSubclass(this->_hWnd, this->_WndProc, 1, (DWORD_PTR)this);
}
LRESULT CALLBACK MyButton::_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSub, DWORD_PTR dwRef)
{
// MessageBox(NULL, L"TEST1", L"TEST1", MB_OK | MB_ICONINFORMATION);
MyButton *pThis = (MyButton*)dwRef;
switch (uMsg)
{
case WM_COMMAND:
MessageBox(NULL, L"TEST2", L"TEST2", MB_OK | MB_ICONINFORMATION);
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
The function is called ("TEST1" is shown) but when I click the button, I don't see "TEST2". I also tried with WM_CREATE and it doesn't work either. I don't know what messages are passed to _WndProc

WM_VSCROLL / WM_HSCROLL message from TrackBar is not sent to the subclassed MessageHandler. Why?

I have a crazy issue. I subclassed buttons, richedits, checkboxes,.. and all seems working fine. But after I subclassed a trackbar I’m having some trouble now. The problem is that my subclassed messagehandler don’t receive the WM_VSCROLL / WM_HSCROLL messages. They are still sent to the parent's messagehandler. WM_PAINT message and some others are successfully sent to the subclassed messagehanlder.
Someone knows what I’m doing wrong? …And maybe knows how to solve this problem? I created a clean project with the following needed code:
#include <windows.h>
#include <CommCtrl.h>
#pragma comment(lib,"comctl32.lib")
//Prototyps
HWND CreateMainWindow(HINSTANCE hInstance);
LRESULT CALLBACK MessageHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
LRESULT CALLBACK SubMessageHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
WNDPROC oldWndProc;
HWND hWnd = 0;
HWND hTrackBar = 0;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){
WNDCLASSEXA wndClass = {sizeof(WNDCLASSEX), CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW, MessageHandler, 0,0, hInstance, LoadIcon(NULL, IDI_WINLOGO),
LoadCursor(NULL, IDC_ARROW),(HBRUSH)GetStockObject(WHITE_BRUSH), NULL, "WindowClass", LoadIcon(NULL, IDI_WINLOGO)};
RegisterClassExA(&wndClass);
//Creat MainWindow
hWnd = CreateWindowExA(NULL, "WindowClass", "Test Windows", WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
100, 100, 400, 300, NULL, NULL, hInstance, NULL);
//Creat Trackbar
INITCOMMONCONTROLSEX initCtrlEx;
initCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
initCtrlEx.dwICC = ICC_BAR_CLASSES;
if (InitCommonControlsEx(&initCtrlEx)){
hTrackBar = CreateWindowExA(NULL,TRACKBAR_CLASSA, "TrackBar_Test", WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | TBS_AUTOTICKS |
TBS_ENABLESELRANGE | TBS_VERT | TBS_BOTH, 10, 10, 50, 200, hWnd, NULL, hInstance, NULL);
oldWndProc = (WNDPROC)SetWindowLongPtrA(hTrackBar, GWLP_WNDPROC, (LONG_PTR)SubMessageHandler); //Subclassing messagehandler
}
//Message loop
MSG msg;
while (GetMessageA(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
return 0;
}
LRESULT CALLBACK SubMessageHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch (msg){
case WM_VSCROLL: //callback is subclassed but WM_VSCROLL is not send. why?
MessageBoxA(hWnd, "WM_VSCROLL sent (to SubMessageHandler)", "Test", MB_OK);
break;
}
if (oldWndProc != 0)
return CallWindowProcA(oldWndProc, hwnd, msg, wParam, lParam);
else
return DefWindowProcA(hwnd, msg, wParam, lParam);
}
LRESULT CALLBACK MessageHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch (msg){
case WM_VSCROLL: //Why the hell is the Trackbar WM_VSCROLL still sent here to the parent callback!?!?
MessageBoxA(hWnd, "WM_VSCROLL sent (to Parent)", "Test", MB_OK);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
}
return DefWindowProcA(hwnd, msg, wParam, lParam);
}
From the MSDN documentation on Trackbar controls:
A trackbar notifies its parent window of user actions by sending the
parent a WM_HSCROLL or WM_VSCROLL message.
The Trackbar's contract is to notify the parent window with WM_HSCROLL/WM_VSCROLL. The Trackbar control generates and sends those messages; it does not receive them.
Also note that the Default Trackbar Message Processing section does not list WM_HSCROLL/WM_VSCROLL (but does list WM_LBUTTONDOWN, WM_MOUSEMOVE, WM_LBUTTONUP, WM_KEYDOWN, WM_KEYUP, which are the raw messages it would need to handle to handle interaction).
As for what to do about it, it probably depends on exactly what you want to do. You could try subclassing and intercepting all of the user input messages, but that seems like a lot of work and is potentially brittle. My recommendation would be to have the parent window explicitly reflect WM_HSCROLL/WM_VSCROLL back to your custom Trackbar control.