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;
};
Ken White
  • 123,280
  • 14
  • 225
  • 444
ProtectedVoid
  • 1,293
  • 3
  • 17
  • 42

5 Answers5

21

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);
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 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 pfnsubclass – Daniel Peedah May 28 '19 at 15:36
  • @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. – Remy Lebeau May 28 '19 at 16:25
  • 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. – Daniel Peedah May 29 '19 at 09:13
  • Note that `SetWindowSubclass()` can be called only from the subclassed window thread. – hldev May 14 '22 at 19:07
  • @hldev the same is true with `GWL_WNDPROC`, too. You can't subclass a window across thread boundaries, regardless of which technique is used. – Remy Lebeau May 14 '22 at 20:48
  • 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. – hldev May 17 '22 at 23:15
  • 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_" – hldev May 17 '22 at 23:20
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.

  • Does it require a cast, as shown in @ProtectedVoid's answer? – user3347392 Jul 28 '15 at 07:38
  • Well, yes, it does. But that's only part of the story. Remy covers the issue well. Have you much experience of subclassing windows? – David Heffernan Jul 28 '15 at 08:06
  • 1
    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. – user3347392 Jul 28 '15 at 08:41
  • setWindowLong does not take LONG_PTR it takes a LONG. setWindowLong(hwnd,GWL_WNDPROC,(LONG)newWindowProcedure); –  Jul 17 '19 at 23:47
0

A simple cast does the job.

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

Otherwise It would be incompatible types: LRESULT and LONG.

ProtectedVoid
  • 1,293
  • 3
  • 17
  • 42
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);
   }
}
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.

William
  • 309
  • 1
  • 9