在 Windows 应用程序中获取鼠标位置

Getting mouse position in a Windows application

我正在使用 DirectX11 编写一个简单的游戏引擎,我正在使用 Win32 API 创建一个 window 并处理用户输入。我正在实施一个光线投射例程来在地形上拾取和放置实体,并且一切正常,除了当我尝试将某些东西放在地面上时,它在顶部和左侧有一个奇怪的偏移:

经过相当多的调试后我发现我的代码工作正常,问题是我发送到光线投射的 window 坐标 class/function:

LRESULT Game::GameWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_LBUTTONDOWN:
            /*MessageBox(mWindow, L"left mouse button clicked", L"CLICK", MB_OK);*/
            Timer::GetInstance()->Stop();
------->    Input::GetInstance()->Place(lParam & 0xFFFF, lParam >> 16 & 0xFFFF, mCamera, mEntity, mTerrain, mRenderer);
            return 0;
           // ....
           // .... other cases
    }
}

鼠标光标的坐标不在0和window width/height之间(我的是1024 X 768),但是最大宽度是1004,最大高度是718(更多or less when I click on the right bottom of the window), 这就是我在调试应用程序时读到的内容。

最后证明,如果我在我的代码中使用这些宽度和高度,对象就会被放置在它们应该放置的位置。 所以我的问题是,为什么坐标没有覆盖整个 window 大小?我错过了什么/做错了什么吗?我怎样才能得到正确的坐标?

编辑

当然,我的函数需要客户区坐标,window 内鼠标光标的坐标。

编辑编辑

这是我用来创建 window

的代码
void Game::InitializeWindow(HINSTANCE hInstance)
{
    WNDCLASS windowClass = {};
    windowClass.hInstance = hInstance;
    windowClass.lpfnWndProc = &WndProc;
    windowClass.lpszClassName = L"wndClass";
    windowClass.lpszMenuName = NULL;
    windowClass.style = CS_HREDRAW | CS_VREDRAW;
    windowClass.cbClsExtra = 0;
    windowClass.cbWndExtra = 0;
    windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
    windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    windowClass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
    RegisterClass(&windowClass);

    mWindow = CreateWindow(L"wndClass", L"DirectX 11 Engine", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, DisplayManager::GetInstance()->GetDisplayWidth(), DisplayManager::GetInstance()->GetDisplayHeight(), 0, 0, hInstance, 0);
    if (!mWindow)
        ErrorBox(L"window creation failed");

    ShowWindow(mWindow, SW_SHOW);
    UpdateWindow(mWindow);
}

您从 WM_LBUTTONDOWN 收到的坐标是客户坐标,并且是正确的。这里真正的问题源于window区客户区之间的差异。

window 区域包含整个 window,包括标题栏和边框。您将从 GetWindowRect 获得的 RECT 恰好大到足以覆盖 window 的每个像素,因此在屏幕上叠加该大小的黑框会导致整个 window, 包括标题栏和边框,消失。

然而,客户区是 内部 边界 Windows 为您创建的区域。您将从 GetClientRect 获得的 RECT 足够大,再次在屏幕上叠加该大小的黑框将覆盖整个 3D 场景,但标题栏和边框仍然可见。

这里的理论和定义都很好,但是现在我们需要解决问题。事实证明,除了 Remy Lebeau 指出的 API 合规性问题(使用 GET_X_LPARAM()/GET_Y_LPARAM() 而不是移动)之外,您在 window 过程中的代码是非常好,不需要修改。您引入错误的实际点是在 window 创建代码期间。

nWidthnHeight 参数到 CreateWindow 指定 window 的宽度和高度,而不是客户端区域。因此,您的客户区会更小。但是,您希望客户区具有这些宽度。

事实证明,这种情况很常见,Windows 2000 添加了一个函数来为给定的客户区域生成适当的 window 宽度和高度:AdjustWindowRect。要使用它,请将您的 CreateWindow 调用更改为以下内容:

RECT windowRect;
windowRect.top = 0;
windowRect.left = 0;
windowRect.right = DisplayManager::GetInstance()->GetDisplayWidth();
windowRect.bottom = DisplayManager::GetInstance()->GetDisplayHeight();

AdjustWindowRect(&windowRect, WS_OVERLAPPEDWINDOW, FALSE);

mWindow = CreateWindow(L"wndClass", 
                       L"DirectX 11 Engine", 
                       WS_OVERLAPPEDWINDOW, 
                       CW_USEDEFAULT, 
                       CW_USEDEFAULT, 
                       windowRect.right, 
                       windowRect.bottom, 
                       0, 
                       0, 
                       hInstance, 
                       0);

这将偏移您的坐标以确保客户区的大小恰好是 DisplayManager 认为应该的大小。

编辑

如果您有扩展的 window 样式,AdjustWindowRectEx 允许您将这些扩展的 window 样式指定为函数的附加第四个参数。