C++ Changing HWND Window Procedure in runtime

ProtectedVoid picture ProtectedVoid · Jul 27, 2015 · Viewed 12.4k times · Source

I'm working in an IDE which creates a hwnd and its respective WndProc LRESULT CALLBACK. I need to change the WndProc to a custom one.

I've read that SetWindowLong would do the job but I can't find any working example. For example:

HWND hwnd; //My window

SetWindowLong(hwnd, GWL_WNDPROC, myNewWndProc);

Third parameter for SetWindowLong is a Long as the name of the function names it. How can I make a reference from my WndProc function to a Long?

My WndProc:

LRESULT CALLBACK WndProcedure(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){

msg_dev(toString(uMsg));

switch(uMsg){

    case WM_MOUSEMOVE:
        SetCursor(LoadCursor(NULL, IDC_HAND));
        break;

    case WM_LBUTTONDOWN:
        msg_dev("Button down!");
        break;

    default:
        DefWindowProc(hwnd, uMsg, wParam, lParam);
}

return 0;
};

Answer

Remy Lebeau picture Remy Lebeau · Jul 27, 2015

You need to use something like this:

WNDPROC prevWndProc;
...
prevWndProc = (WNDPROC) SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR)&myNewWndProc);
...    
LRESULT CALLBACK myNewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    msg_dev(toString(uMsg));

    switch(uMsg)
    {
        case WM_MOUSEMOVE:
            SetCursor(LoadCursor(NULL, IDC_HAND));
            break;

        case WM_LBUTTONDOWN:
            msg_dev("Button down!");
            break;
    }

    return CallWindowProc(prevWndProc, hwnd, uMsg, wParam, lParam);
}

See this article:

When you subclass a window, it's the original window procedure of the window you subclass you have to call when you want to call the original window procedure

That being said, you should use SetWindowSubclass() instead of SetWindowLongPtr(). See this article:

Safer subclassing

For example:

SetWindowSubclass(hwnd, &mySubClassProc, 1, 0);
...    
LRESULT CALLBACK mySubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    msg_dev(toString(uMsg));

    switch(uMsg)
    {
        case WM_MOUSEMOVE:
            SetCursor(LoadCursor(NULL, IDC_HAND));
            break;

        case WM_LBUTTONDOWN:
            msg_dev("Button down!");
            break;

        case WM_NCDESTROY:
            RemoveWindowSubclass(hWnd, &mySubClassProc, 1);
            break;
    }

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