【问题标题】:C++ Changing HWND Window Procedure in runtimeC++ 在运行时更改 HWND 窗口过程
【发布时间】:2015-10-17 08:22:06
【问题描述】:

我正在创建一个hwnd 及其各自的WndProc LRESULT CALLBACK 的IDE。我需要将WndProc 更改为自定义的。

我读到SetWindowLong 可以完成这项工作,但我找不到任何可行的示例。例如:

HWND hwnd; //My window

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

【问题讨论】:

  • 您说的是子分类。为此使用SetWindowSubclass

标签: c++ winapi runtime wndproc hwnd


【解决方案1】:

你需要使用这样的东西:

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

见这篇文章:

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

话虽如此,您应该使用SetWindowSubclass() 而不是SetWindowLongPtr()。见这篇文章:

Safer subclassing

例如:

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

【讨论】:

  • 我在尝试你的新风格时遇到错误,我收到与 defsubclassproc 和 setwindowsubclass 相关的外部符号错误,而且,我不能将我的子类用作 setWindowSubClass 中的参数,因为它说它是与 pfnsubclass 类型不兼容
  • @DanielPeedah "尝试你的新风格时出错" - 对我来说很好。 “I am given external symbol errors” - 这些是链接器错误,而不是编译器错误。您是否链接到comctl32.lib? “我不能将我的子类用作 setWindowSubClass 中的参数,因为它说它不兼容” - 那么您的过程没有正确声明。 SetWindowSubclass() 使用的过程与GWL_WNDPROC 使用的过程具有不同的参数。阅读我在答案中链接到的文档,并密切注意我的答案中显示的示例。
  • 是因为我使用 VS13 工具集构建项目吗?我想这就是为什么我收到 setWindowSubClass 和 DefSubClassProc 的符号错误的原因。由于智能感知直到运行时才给我任何错误,这是我最好的猜测。
【解决方案2】:

一个简单的演员就可以完成这项工作。

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

否则将是不兼容的类型:LRESULT 和 LONG。

【讨论】:

    【解决方案3】:

    SetWindowLong() 的 MSDN 文档指出 GWL_WNDPROC

    为窗口过程设置一个新地址。

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

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

    另请注意备注部分指出:

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

    【讨论】:

    • 是否需要演员表,如@ProtectedVoid 的回答所示?
    • 嗯,是的,确实如此。但这只是故事的一部分。雷米很好地解决了这个问题。你有很多子类化窗口的经验吗?
    • 我在我的答案中添加了一个 '(LONG_PTR)' 。子类化窗口不是我有太多经验的东西。我相信这个答案现在完全回答了原始问题,但@Remy 的答案可能更有用。
    • setWindowLong 不需要 LONG_PTR 它需要 LONG。 setWindowLong(hwnd,GWL_WNDPROC,(LONG)newWindowProcedure);
    【解决方案4】:

    您可以使用 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);
       }
    }
    

    【讨论】:

      【解决方案5】:

      您必须使用 SetWindowLongPtr(在 32 位上是一个宏,但在 64 位上是一个单独的函数)来确保与 32 位和 64 位系统的兼容性。

      语法如下:

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

      注意使用SetWindowLongPtr代替SetWindowLong,GWLP_WNDPROC用作nIndex常量。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-08-14
        • 1970-01-01
        • 1970-01-01
        • 2014-06-22
        • 1970-01-01
        • 2014-05-24
        • 1970-01-01
        相关资源
        最近更新 更多