Changing HWND Window Procedure in runtime
Asked Answered
D

5

11

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

The 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;
};
Diacetylmorphine answered 27/7, 2015 at 8:23 Comment(1)
You're talking about sub-classing. Use SetWindowSubclass for this.Ridotto
H
22

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(). Let it handle this for you. See this article for more details:

Safer subclassing

For example:

#define MY_SUBCLASS_ID 1

SetWindowSubclass(hwnd, &mySubClassProc, MY_SUBCLASS_ID, 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, uIdSubclass);
            break;
    }

    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
Hereunder answered 27/7, 2015 at 17:52 Comment(7)
I get an error trying out that new style of yours, I am given external symbol errors to do with defsubclassproc and setwindowsubclass , also , I cannot use my subclass as a parameter in setWindowSubClass as it says it is not compatible with type pfnsubclassUnsteel
@DanielPeedah "I get an error trying out that new style of yours" - works fine for me. "I am given external symbol errors" - those are linker errors, not compiler errors. Are you linking to comctl32.lib? "I cannot use my subclass as a parameter in setWindowSubClass as it says it is not compatible" - then your procedure is not declared correctly. The procedure used with SetWindowSubclass() has different parameters than the procedure used with GWL_WNDPROC. Read the documentation I linked to in my answer, and pay closer attention to the examples shown in my answer.Hereunder
Is it due to me building the project with the VS13 toolset? I'd presume that is why I am getting symbol errors for setWindowSubClass and DefSubClassProc. As intellisense doesn't give me any errors till at run time, that is my best guess.Unsteel
Note that SetWindowSubclass() can be called only from the subclassed window thread.Marrowfat
@Marrowfat the same is true with GWL_WNDPROC, too. You can't subclass a window across thread boundaries, regardless of which technique is used.Hereunder
MSDN says that only for SetWindowSubclass, for SetWindowLongPtr they say "Calling SetWindowLongPtr with the GWLP_WNDPROC index creates a subclass of the window class used to create the window. An application can subclass a system class, but should not subclass a window class created by another process." So they say nothing about threads boundaries, but about process boundaries (it's allowed, but you should not). Try yourself, I could subclass across thread boundaries with SetWindowLongPtr, but SetWindowSubclass returned error.Marrowfat
MSDN also says "The SetWindowLongPtr function fails if the process that owns the window specified by the hWnd parameter is at a higher process privilege in the UIPI hierarchy than the process the calling thread resides in"Marrowfat
V
0

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.

Vaticinate answered 27/7, 2015 at 8:33 Comment(4)
Does it require a cast, as shown in @ProtectedVoid's answer?Vaticinate
Well, yes, it does. But that's only part of the story. Remy covers the issue well. Have you much experience of subclassing windows?Dermatology
I have added a '(LONG_PTR)' cast into my answer. Subclassing windows is not something that I have much experience with. I believe that this answer now answers the original question fully, but @Remy's answer is probably more useful.Vaticinate
setWindowLong does not take LONG_PTR it takes a LONG. setWindowLong(hwnd,GWL_WNDPROC,(LONG)newWindowProcedure);Elda
D
0

A simple cast does the job.

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

Otherwise It would be incompatible types: LRESULT and LONG.

Diacetylmorphine answered 27/7, 2015 at 8:37 Comment(0)
E
0

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);
   }
}
Elda answered 18/7, 2019 at 0:22 Comment(0)
R
0

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.

Riba answered 1/1, 2020 at 6:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.