0

I have a Windows system with multiple monitors connected as extended desktop. Only the primary monitor is physically visible to the user, so I want to trap the mouse on that monitor.

There seems to be one simple solution using the ClipMouse API function, as described in trap-mouse-in-wpf:

[DllImport("user32.dll")]
static extern void ClipCursor(ref System.Drawing.Rectangle rect);

private void TrapMouse()
        {
            System.Drawing.Rectangle r = new System.Drawing.Rectangle(x, y, width, height);
            ClipCursor(ref r);
        }

However, the mouse easily breaks free, e.g. when changing the program with alt-tab or when touching one of the secondary touch screens.

Is there any way to reliably and permanently capture the mouse on one monitor?

Community
  • 1
  • 1
Frank im Wald
  • 896
  • 1
  • 11
  • 28

1 Answers1

0

Here's some simple Windows Forms code you can try. It uses the SetWindowsHookEx function to set a global mouse hook. In the hook method it checks to see whether the mouse coordinates are within the bounds of the primary screen and it adjusts the coordinates, if necessary. When you run this code, the mouse cursor will still be able to leave the primary screen area as long as you are just moving the mouse, but it will jump back as soon as a click event happens. You might want to combine my code with your ClipCursor technique to prevent this from happening.

public partial class Form1 : Form
{
    private const int WH_MOUSE_LL = 14;

    private delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", EntryPoint = "SetWindowsHookEx", SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);

    Rectangle _ScreenBounds;

    HookProc _HookProc;

    public Form1()
    {
        InitializeComponent();

        _ScreenBounds = Screen.PrimaryScreen.Bounds;
        _HookProc = HookMethod;
        IntPtr hook = SetWindowsHookEx(WH_MOUSE_LL, _HookProc, GetModuleHandle("user32"), 0);
        if (hook == IntPtr.Zero) throw new System.ComponentModel.Win32Exception();
    }

    private int HookMethod(int code, IntPtr wParam, IntPtr lParam)
    {
        if (Cursor.Position.X < _ScreenBounds.Left)
        {
            Cursor.Position = new Point(_ScreenBounds.Left, Cursor.Position.Y);
        }
        else if (Cursor.Position.X > _ScreenBounds.Right)
        {
            Cursor.Position = new Point(_ScreenBounds.Right - 1, Cursor.Position.Y);
        }

        if (Cursor.Position.Y < _ScreenBounds.Top)
        {
            Cursor.Position = new Point(Cursor.Position.X, _ScreenBounds.Top);
        }
        else if (Cursor.Position.Y > _ScreenBounds.Bottom)
        {
            Cursor.Position = new Point(Cursor.Position.X, _ScreenBounds.Bottom - 1);
        }

        return 0;
    }
}
Adrian Theodorescu
  • 11,664
  • 2
  • 23
  • 30
  • Thanks for this snippet! We played around with it, but as it kicks in only after pressing or releasing a mouse button, it doesn't really solve our problem. So I have to go on finding something else. – Frank im Wald Jul 23 '14 at 14:53