C++ Changing HWND Window Procedure in runtime

2020-07-16 09:05发布

问题:

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

回答1:

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


回答2:

A simple cast does the job.

SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG_PTR)&myNewWndProc);

Otherwise It would be incompatible types: LRESULT and LONG.



回答3:

The MSDN documentation for SetWindowLong() states that GWL_WNDPROC

Sets a new address for the window procedure.

This means that your third parameter should be a pointer to a function. Your SetWindowLong() call should therefore look like this:

SetWindowLong(hwnd, GWL_WNDPROC, (LONG_PTR)&myNewWndProc);

Note also the Remarks section that states:

An application must pass any messages not processed by the new window procedure to the previous window procedure by calling CallWindowProc.



回答4:

You can use setWindowLong to address your problem.

setWindowLong(hwnd,GWL_WNDPROC,(LONG)newWindowProcedure);

However you would be setting the window procedure twice. Once with the IDE default and then to yours. What you need to dobis set the window procedure when the window is being REGISTERED.

#include <windows.h>


void registerWindow();
void createWindow();
void messageLoop();


int main()
{
 registerWindow();
 createWindow();
 messageLoop();
}


LRESULT CALLBACK myWindowProcedure(HWND hwnd,UINT msg,WPARAM wparam,LPARAM lparam)
{
 return DefWindowProc(hwnd,msg,wparam,lparam);
}

void registerWindow()
{
 /** This is the important part.
    * Find this part in your code.
    * Set WNDCLASS::lpfnWndProc to what ever 
    * window procedure you want.
 **/

 WNDCLASS wc = {};

 wc.lpfnWndProc   = myWindowProcedure;
 wc.hInstance     = hInstance;
 wc.lpszClassName = "CLASS NAME";

 RegisterClass(&wc);

 // WARNING: Your app will crash at runtime if the 
 // windows procedure is "NOT PROPER"
}

void createWindow()
{
 auto hwnd = CreateWindowEx(
    0,                              // Optional window styles.
    "CLASS NAME",                     // Window class
    "Learn to Program Windows",    // Window text
    WS_OVERLAPPEDWINDOW,            // Window style

    // Size and position
    CW_USEDEFAULT, 
    CW_USEDEFAULT, 
    CW_USEDEFAULT, 
    CW_USEDEFAULT,

    NULL,       // Parent window    
    NULL,       // Menu
    HINSTANCE(),  // Instance handle
    NULL        // Additional application data
    );

   ShowWindow(hwnd, nCmdShow
}

void messageLoop()
{
    MSG msg;
    while( GetMessage(&msg, NULL, 0, 0) )
   {
    TranslateMessage(&msg); 
    DispatchMessage(&msg);
   }
}


回答5:

You have to use SetWindowLongPtr (which on 32-bit is a macro but a separate function on 64-bit) to ensure compatibility with both 32- and 64-bit systems.

Syntax would be as follows:

SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)&myNewWndProc);

Note SetWindowLongPtr is used instead of SetWindowLong, and GWLP_WNDPROC is used as the nIndex constant.