在多显示器环境中计算 SendInput() 的归一化坐标

Calculate normalized coordinates for SendInput() in a multi-monitor environment

我想以编程方式将鼠标输入发送到我连接的任何显示器。

我知道我可以使用 SendInput 函数来执行此操作,但我当前的方法不适用于多显示器设置。

我正在使用以下方法:

  1. 获取要点击的点相对于当前屏幕的像素坐标;
  2. 获取当前屏幕左上角像素的像素坐标,使用具有适当 HMONITOR 句柄的 GetMonitorInfoW 函数;
  3. 获取相对于整个虚拟屏幕的像素坐标:为此,我们将当前屏幕边缘(左上角像素)的坐标与要点击的点的坐标相加;
  4. 将得到的像素坐标转换成绝对坐标(0-65535),以供SendInput函数使用;
  5. 使用 SendInput 函数与 MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK 标志和相对于整个虚拟桌面的绝对坐标。

但是,在我当前的代码中,鼠标输入定位错误。 我对如何正确计算绝对坐标感到困惑。

目前,我确实使用 another question 上讨论的公式检索绝对坐标。 要获得虚拟屏幕的 width/height,我使用 GetSystemMetrics 和 SM_CXVIRTUALSCREEN/SM_CYVIRTUALSCREEN 参数。

// ________________________________________________
//
// GetAbsoluteCoordinate
//
// PURPOSE: 
// Convert pixel coordinate to absolute coordinate (0-65535).
//
// RETURN VALUE:
// Absolute Coordinate
// ________________________________________________
//
UINT16 GetAbsoluteCoordinate(INT PixelCoordinate, INT ScreenResolution)
{
    UINT16 AbsoluteCoordinate = ( (65536 * PixelCoordinate) / ScreenResolution ) + 1;
    return AbsoluteCoordinate;
}


// ________________________________________________
//
// GetAbsoluteCoordinates
//
// PURPOSE: 
// Retrieve virtual screen absolute coordinates from pixel coordinates.
//
// PARAMETERS:
// x, y coordinates passed by reference. These will be changed by the function and used as return values.
//
// RETURN VALUE:
// None (see parameters)
// ________________________________________________
//
void GetAbsoluteCoordinates(INT32 &X, INT32 &Y)
{
    // Get multi-screen coordinates
    MONITORINFO MonitorInfo = { 0 };
    MonitorInfo.cbSize = sizeof(MonitorInfo);
    if (GetMonitorInfoW(hMonitor, &MonitorInfo))
    {
        // 1) Get pixel coordinates of topleft pixel of target screen, relative to the virtual desktop ( coordinates should be 0,0 on Main screen);
        // 2) Get pixel coordinates of mouse cursor, relative to the target screen;
        // 3) Sum topleft margin pixel coordinates with mouse cursor coordinates;
        X = MonitorInfo.rcMonitor.left + X;
        Y = MonitorInfo.rcMonitor.top + Y;

        // 4) Transform the resulting pixel coordinates into absolute coordinates.
        X = GetAbsoluteCoordinate(X, GetSystemMetrics(SM_CXVIRTUALSCREEN));
        Y = GetAbsoluteCoordinate(Y, GetSystemMetrics(SM_CYVIRTUALSCREEN));
    }
} 



// ________________________________________________
//
// SendMouseInput
//
// PURPOSE: 
// Send mouse input, supporting any of the connected displays
//
// PARAMETERS:
// X, Y are pixel coordinates, relative to the current screen.
// ________________________________________________
//
 void SendMouseInput(INT X, INT Y) 
  {
     INPUT Input;
     GetAbsoluteCoordinates(X, Y);
     memset(&Input, 0, sizeof(INPUT)); 
     Input.type = INPUT_MOUSE;
     Input.mi.dx = X;
     Input.mi.dy = Y;
     Input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | MOUSEEVENTF_MOVE | MOUSEEVENTF_LEFTDOWN;
     SendInput(1, &Input, sizeof(Input));
  }
  1. Get the pixel coordinates relative to the whole virtual screen: to do this, we sum the coordinates of the current screen margin (topleft pixel) with the coordinates of the point to click;

您似乎在使用相对于显示器的坐标(X,Y)。你是怎么得到坐标的?

如果是这样,那么样本基本没问题。如果你的鼠标坐标是通过类似于GetCursorPos的方式获取的,那么你就不需要计算相对于虚拟屏幕的坐标(它是)。

该示例几乎对我有用,我更改了一些代码如下:

#include <windows.h>
#include <iostream>
using namespace std;
// ________________________________________________
//
// GetAbsoluteCoordinate
//
// PURPOSE: 
// Convert pixel coordinate to absolute coordinate (0-65535).
//
// RETURN VALUE:
// Absolute Coordinate
// ________________________________________________
//
INT GetAbsoluteCoordinate(INT PixelCoordinate, INT ScreenResolution)
{
    INT AbsoluteCoordinate = MulDiv(PixelCoordinate, 65535, ScreenResolution);
    return AbsoluteCoordinate;
}

void GetAbsoluteCoordinates(HMONITOR hMonitor, INT32& X, INT32& Y)
{
    // Get multi-screen coordinates
    MONITORINFO MonitorInfo = { 0 };
    MonitorInfo.cbSize = sizeof(MonitorInfo);
    if (GetMonitorInfoW(hMonitor, &MonitorInfo))
    {
        // 1) Get pixel coordinates of topleft pixel of target screen, relative to the virtual desktop ( coordinates should be 0,0 on Main screen);
        // 2) Get pixel coordinates of mouse cursor, relative to the target screen;
        // 3) Sum topleft margin pixel coordinates with mouse cursor coordinates;
        X = MonitorInfo.rcMonitor.left + X;
        Y = MonitorInfo.rcMonitor.top + Y;

        // 4) Transform the resulting pixel coordinates into absolute coordinates.
        X = GetAbsoluteCoordinate(X, GetSystemMetrics(SM_CXVIRTUALSCREEN));
        Y = GetAbsoluteCoordinate(Y, GetSystemMetrics(SM_CYVIRTUALSCREEN));
    }
}

void SendMouseInput(HMONITOR hMonitor, INT X, INT Y)
{
    INPUT Input[2];
    GetAbsoluteCoordinates(hMonitor, X, Y);
    memset(Input, 0, sizeof(INPUT));
    Input[0].type = Input[1].type = INPUT_MOUSE;
    Input[0].mi.dx = Input[1].mi.dx = X;
    Input[0].mi.dy = Input[1].mi.dy = Y;
    Input[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_MOVE | MOUSEEVENTF_VIRTUALDESK;
    Input[1].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP | MOUSEEVENTF_MOVE | MOUSEEVENTF_VIRTUALDESK;

    SendInput(2, Input, sizeof(INPUT));
}
BOOL CALLBACK Monitorenumproc(
    HMONITOR Arg1,
    HDC Arg2,
    LPRECT Arg3,
    LPARAM Arg4)
{
    SendMouseInput(Arg1, 725, 85);
    return TRUE;
}
int main(void)
{
    EnumDisplayMonitors(NULL,NULL, Monitorenumproc,0);
    return 0;
}

结果:

我有 2 台具有相同显示分辨率 (1920 x 1080) 的显示器来测试:

示例将在每台显示器上的相同位置点击。

进一步阅读:The Virtual Screen