【问题标题】:How to use WM_MOUSEMOVE LParam value outside of WndProc in a separate translation unit?如何在单独的翻译单元中使用 WndProc 之外的 WM_MOUSEMOVE LParam 值?
【发布时间】:2020-08-19 02:40:06
【问题描述】:

也许这只是一个代码组织问题,也许我缺乏一些极其基础的 C++ 知识——我已经尝试了几个小时来找到一个合理的解决方案,但似乎没有任何效果,所以我转向互联网。

我正在编写一个用于游戏开发的 Direct2D 应用程序,当出现 WM_MOUSEMOVE 消息时,我试图从 WndProc lParam 获取鼠标坐标,正如 MSDN article here 中所概述和推荐的那样。以下是项目相关部分的配置方式。

window.h

struct Window {
    HWND _hwnd;

    bool Initialize(int width, int height);
    void RunMessageLoop();
};

window.cpp

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_MOUSEMOVE:
        int xPosAbsolute = GET_X_PARAM(lParam); // as is suggested by MSDN
        int yPosAbsolute = GET_Y_PARAM(lParam); // as is suggested by MSDN
        ...
        break;
    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

bool Window::Initialize(int width, int height) {
    WNDCLASS wc;
    wc.lpfnWndProc = WndProc;
    // etc...
}

void Window::RunMessageLoop() {
    MSG Msg;
    while (true)
    {
        GetMessage(&Msg, NULL, 0, 0);
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    };
}

ma​​in.cpp

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow)
{
    // ...
    while (true) {
        int absolute_mouse_pos_x = ???;
        int absolute_mouse_pos_y = ???;
        // etc...
    }
    return 0;
}

我的问题:如何在 ma​​in.cpp 中合理分配(和更新)absolute_mouse_pos_xabsolute_mouse_pos_y使用 window.cppWndProc 函数的 xPosAbsolute/yPosAbsolute 值?

首先想到的是实例化 WndProc 函数,以便它可以访问 Window 结构的成员,但这是不可能/不切实际的,因为成员函数的签名具有隐藏的“this”参数, as other answers on Stack Overflow such as this one have detailed.

之后,我尝试在 window.h 中创建全局变量,在 window.cpp 的 WndProc 函数中分配它们,并在 main.cpp 中引用它们。 main.cpp 可以使用两个全局变量的初始值,但是后来用新值更新这些全局变量似乎对 main.cpp 完全不可见(我想知道这些全局变量是否是隐式只读的,但这可能只是缺少我的理解/用户错误)。除了这种行为之外,普遍的看法是除非绝对必要,否则不应使用全局变量,而且对于看似简单的事情必须使用全局变量似乎很奇怪。没有更简单/更好的方法吗?

谢谢!

【问题讨论】:

  • 查看Managing Application State 的示例,以及Use object method as WinApi WndProc callback 和的最佳答案。
  • 你当然可以使用全局变量,尽管你必须正确地做——大概你没有(尽管你没有展示你做了什么)。另一种方法是忽略WM_MOUSEMOVE 消息,只要你想要当前光标位置就调用GetCursorPos()
  • 我觉得可以使用POINT cursor;GetCursorPos(&cursor); 获取当前鼠标位置信息。你能解释一下为什么需要通过 WndProc 传递它吗?
  • @ZhuSong 对于我自己的目的,它不一定需要通过 WndProc 传递,这似乎是基于 MSDN 推荐的用法(至少对于 UI 样式的光标坐标)。感谢大家的帮助。今天早上我尝试了 GetCursorPos() 和 Get/SetWindowLongPtr 方法。我会尝试在我的结果中发布答案,以帮助其他遇到类似问题的人。

标签: c++ windows winapi


【解决方案1】:

这里有一些可能的方法来实现这一点。

  1. GetCursorPos()

这很简单,返回光标的屏幕坐标,而不是窗口内的坐标。根据您正在制作的应用程序,这可能是(不)可取的。您可以使用ScreenToClient() 将这些坐标转换为窗口坐标。

为简单起见,这是一个使用 main.cpp 的示例,但可以在任何其他翻译单元中完成。

ma​​in.cpp

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow)
{
    // ...
    POINT p;
    while (true) {
        GetCursorPos(&p);
        int absolute_mouse_pos_x = p.x;
        int absolute_mouse_pos_y = p.y;
        // etc...
    }
    return 0;
}
  1. Get/SetWindowLongPtr

这更复杂,但会为您提供光标的窗口坐标(通过 WM_MOUSEMOVE 消息的 lParam),并允许您获取/设置您选择的自定义结构的状态WndProc。以下是实现此功能所需的 window.h 和 window.cpp 更改,以及从单独的翻译单元 (main.cpp) 访问这些变量的示例。

window.h

struct Window {
    HWND _hwnd;
    int mouse_x;
    int mouse_y;

    bool Initialize(int width, int height);
    // etc..
};

window.cpp

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    Window* window;
    if (msg == WM_CREATE)
    {
        CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
        window = reinterpret_cast<Window*>(pCreate->lpCreateParams);
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)window);
    }
    else
    {
        LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
        window = reinterpret_cast<Window*>(ptr);
    }


    switch (msg)
    {
    case WM_MOUSEMOVE:
        window->mouse_x = GET_X_PARAM(lParam);
        window->mouse_y = GET_Y_PARAM(lParam);
        break;
    // etc...
    }
    return 0;
}

bool Window::Initialize(int width, int height) {
    WNDCLASS wc;
    wc.lpfnWndProc = WndProc;
    // etc...

    CreateWindowEx(.., this); // add the "this" pointer as the last argument to CreateWindowEx()
}

ma​​in.cpp

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, INT nCmdShow)
{
    // ...
    Window w;
    w.Initialize(width, height);
    while (true) {
        int absolute_mouse_pos_x = w.mouse_x;
        int absolute_mouse_pos_y = w.mouse_y;
        // etc...
    }
    return 0;
}

【讨论】:

  • 请注意,您可以使用ScreenToClient() 将屏幕坐标转换为特定窗口的客户端坐标。
  • 很高兴您得到了解决方案,感谢您的分享,如果您将它们标记为答案,我将不胜感激,这将对其他社区有益。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-01
  • 2023-04-05
相关资源
最近更新 更多