运行时更改HWND窗口过程

11

我正在使用一个IDE,它创建了一个hwnd以及相应的WndProc LRESULT CALLBACK。我需要将WndProc更改为自定义的。

我已经阅读过SetWindowLong可以完成此任务,但是我找不到任何可行的示例。例如:

HWND hwnd; //我的窗口

SetWindowLong(hwnd,GWL_WNDPROC,myNewWndProc);

对于SetWindowLong的第三个参数是一个Long,正如函数名称所述。我怎么才能将我的WndProc函数引用转换成一个Long

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

4
您在谈论子类化。使用 SetWindowSubclass 进行此操作。 - Jonathan Potter
5个回答

21

你需要使用类似以下的内容:

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

请看这篇文章:

当你子类化一个窗口时,当你想调用原始的窗口过程时,你需要调用被子类化的窗口的原始窗口过程。

话虽如此,你应该使用SetWindowSubclass()而不是SetWindowLongPtr()。 让它来处理这个问题。有关更多详细信息,请参见本文:

更安全的子类化

例如:

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

我尝试使用你的新样式时出现了错误,与 defsubclassproc 和 setwindowsubclass 相关的外部符号错误被提示,而且,我无法将我的子类作为 setWindowSubClass 的参数使用,因为它说它与类型 pfnsubclass 不兼容。 - Daniel Peedah
请注意,SetWindowSubclass() 只能从子类化的窗口线程中调用。 - hldev
@hldev 对于GWL_WNDPROC也是如此。无论使用哪种技术,都无法跨线程边界子类化窗口。 - Remy Lebeau
MSDN指出,仅对于SetWindowSubclass,对于SetWindowLongPtr,它们说:“使用GWLP_WNDPROC索引调用SetWindowLongPtr将创建窗口类的子类,该窗口类用于创建窗口。应用程序可以对系统类进行子类化,但不应对另一个进程创建的窗口类进行子类化。”所以他们没有关于线程边界的说明,而是关于进程边界的说明(允许,但是你“不应该”这样做)。尝试一下,我可以使用SetWindowLongPtr跨越线程边界进行子类化,但是SetWindowSubclass返回错误。 - hldev
MSDN还说:“如果拥有hWnd参数指定的窗口的进程在UIPI层次结构中的进程特权高于调用线程所在的进程,则SetWindowLongPtr函数将失败。” - hldev
显示剩余2条评论

0
一个简单的转换即可完成任务。

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

否则会出现不兼容的类型:LRESULT 和 LONG。

0

关于 SetWindowLong() 函数的 MSDN 文档说明{{link1:},GWL_WNDPROC

设置窗口过程的新地址。

这意味着您的第三个参数应该是一个函数指针。因此,您的 SetWindowLong() 函数调用应该是这样的:

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

请注意备注部分的说明:

应用程序必须通过调用CallWindowProc将未被新窗口过程处理的任何消息传递给先前的窗口过程。


需要进行强制类型转换吗,就像@ProtectedVoid的回答中所示? - user3347392
是的,没错。但这只是故事的一部分。Remy 对这个问题有很好的讲解。你有 subclassing windows 的经验吗? - David Heffernan
1
我在我的答案中添加了一个“(LONG_PTR)”转换。子类化窗口不是我很有经验的事情。我相信这个答案现在完全回答了原始问题,但@Remy的答案可能更有用。 - user3347392
setWindowLong不接受LONG_PTR,而是接受LONG类型。setWindowLong(hwnd,GWL_WNDPROC,(LONG)newWindowProcedure); - user9599745

0
你可以使用 setWindowLong 来解决你的问题。
setWindowLong(hwnd,GWL_WNDPROC,(LONG)newWindowProcedure);

然而,你会设置窗口过程两次。一次是使用IDE的默认值,然后再设置为你自己的值。你需要做的是在窗口被注册时设置窗口过程。
#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

您需要使用SetWindowLongPtr(在32位系统上是宏,在64位系统上是单独的函数)来确保与32位和64位系统兼容。

语法如下:

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

使用SetWindowLongPtr而不是SetWindowLong,并使用GWLP_WNDPROC作为nIndex常量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接