Search code examples
c#sendinputvirtual-screen

Finding virtual screen width using multiple monitors and different DPI


I'm trying to simulate mouse input using SendInput, however I need to determine the virtual screen width in case there are multiple monitors, to set the dx and dy values, for example:

dx = (x * 65536) / SystemInformation.VirtualScreen.Width;
dy = (y * 65536) / SystemInformation.VirtualScreen.Height;

The problem is that the monitors apparently have different DPI values, which results in an incorrect virtual size. For reference, both SystemInformation.VirtualScreen.Size and calls to GetSystemMetrics(SM_CXVIRTUALSCREEN) return the same incorrect values.

I tried using GetSystemMetricsForDPI with no success either.


Solution

  • By default, Windows applies DPI virtualization to all coordinates.

    If you have 2 displays, secondary FullHD on the left with 100% scaling, and primary 4k on the right with 200% DPI scaling, the APIs for screen configuration gonna tell you the desktop has 3840x1080 pixels, the secondary one from [-1920 .. 0], the primary one [0 .. 1920]. To simulate mouse input at the center of the left monitor, you should use X coordinate -960 * 0x10000 / 1920 = -32768 without MOUSEEVENTF_VIRTUALDESK flag, or 960 * 0x10000 / 3840 = 16384 with MOUSEEVENTF_VIRTUALDESK flag

    You can tell Windows to stop messing with DPI in your application, with a manifest.

    If you do that on the same computer, the APIs for screen configuration gonna tell you the desktop has 5760x2160 pixels, the secondary one is the same, in [-1920 .. 0 ], the primary one [ 0 .. 3840 ]. To send mouse input to the center of the left monitor in this case, you should use X coordinate -960 * 0x10000 / 3840 = -16384 without MOUSEEVENTF_VIRTUALDESK flag, or 960 * 0x10000 / 5760 = 10923 with MOUSEEVENTF_VIRTUALDESK flag.