Search code examples
c++winapibuttonwndproc

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.enter image description here

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;
}

Solution

  • 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.