4

I have a laptop with a very sensitive touch pad, and wanted to code a small program that could block the mouse input when I was typing a paper or something.

I didn't think it would be hard to do, considering everything I've seen on low-level hooks, but I was wrong (astounding, right?).

I looked at a few examples, but the examples I've seen either block both keyboard and mouse, or just hide the mouse.

Any help with this would be great.

peterh
  • 11,875
  • 18
  • 85
  • 108
Jess
  • 8,628
  • 6
  • 49
  • 67
  • 5
    Why not just disable that particular mouse using Device Manager? – Jonathon Faust Dec 24 '10 at 06:29
  • 1
    maybe changing sensitivity settings, or as Jon said with a bat script. Some laptops have a button dedicated. The hooks will affect the performance significantly, in case you decide to use them. – Eric Fortis Dec 24 '10 at 06:35
  • Check on your keyboard... most likely you have functionality to stop the input from the mouse pad completely. Usually this is found as a blue icon on the function keys (F1, F2...) – npinti Dec 24 '10 at 06:45
  • I have checked, there isn't an a switch/function key – Jess Dec 24 '10 at 06:53
  • 1
    you can read about the performance issue in the overview of http://msdn.microsoft.com/en-us/library/ms644959(v=vs.85).aspx – Eric Fortis Dec 24 '10 at 13:03

2 Answers2

8

As you mentioned, you can do this using a low-level mouse hook (WH_MOUSE_LL), albeit somewhat incorrectly. What happens when you set a hook is that you'll receive notifications on each mouse input event (WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_MBUTTONDOWN, WM_XBUTTONDOWN, WM_NCXBUTTONDOWN, the equivalent up events for each of those, WM_MOUSEWHEEL, and WM_MOUSEHWHEEL). Once you've finished processing each event, you're supposed to call the CallNextHookEx function, which passes the event information on to the next application in the hook chain. However, if you want to prevent any other program from getting mouse input information, you can just skip calling that function at the end of your hook procedure. The "Remarks" section of the above-linked documentation explains it thusly:

Calling CallNextHookEx is optional, but it is highly recommended; otherwise, other applications that have installed hooks will not receive hook notifications and may behave incorrectly as a result. You should call CallNextHookEx unless you absolutely need to prevent the notification from being seen by other applications.

And as it turns out, low-level mouse hooks aren't actually that difficult in C#. I just coded one up myself, actually. But rather than posting that monstrosity of a library, I'll refer you to the simpler code snippet posted on Stephen Toub's blog, which I've reprinted here with syntax highlighting for convenience:

class InterceptMouse
{
    private static LowLevelMouseProc _proc = HookCallback;
    private static IntPtr _hookID = IntPtr.Zero;

    public static void Main()
    {
        _hookID = SetHook(_proc);
        Application.Run();
        UnhookWindowsHookEx(_hookID);
    }

    private static IntPtr SetHook(LowLevelMouseProc proc)
    {
        using (Process curProcess = Process.GetCurrentProcess())
        using (ProcessModule curModule = curProcess.MainModule)
        {
            return SetWindowsHookEx(WH_MOUSE_LL, proc,
                GetModuleHandle(curModule.ModuleName), 0);
        }
    }

    private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);

    private static IntPtr HookCallback(
        int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 &&
            MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
        {
            MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
            Console.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y);
        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

    private const int WH_MOUSE_LL = 14;

    private enum MouseMessages
    {
        WM_LBUTTONDOWN = 0x0201,
        WM_LBUTTONUP = 0x0202,
        WM_MOUSEMOVE = 0x0200,
        WM_MOUSEWHEEL = 0x020A,
        WM_RBUTTONDOWN = 0x0204,
        WM_RBUTTONUP = 0x0205
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int x;
        public int y;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct MSLLHOOKSTRUCT
    {
        public POINT pt;
        public uint mouseData;
        public uint flags;
        public uint time;
        public IntPtr dwExtraInfo;
    }

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

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
        IntPtr wParam, IntPtr lParam);

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

As I explained above, you'll want to modify his HookCallback method not to call CallNextHookEx once you've finished processing the mouse event, but return something like new IntPtr(1) instead.

EDIT: And yeah, as others mentioned, there are probably other easier, cleaner solutions to this problem. Your trackpad drivers are a great place to look for an option like "Ignore accidental mouse input while typing". If you don't have this option, you're probably using the standard Windows mouse drivers. Try to download the drivers from your trackpad's manufacturer from the laptop manufacturer's website (for what it's worth, most of the non-Apple trackpads I've seen are Synaptics).

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • A good idea, but I can't get it to work. Tried setting the ncode to -1, taking out the return, and setting the method to return void. Any other ideas? I can't even change the mouse button being pressed. – Jess Dec 24 '10 at 07:38
  • 2
    @mazzzzz: I suggested modifying the return statement to: `return new IntPtr(1);`. This works fine for me. As I indicated above, I just finished writing and testing a library using the exact same method. There's no need to modify the `nCode` value at all, and you can't just go changing the return type of the method. – Cody Gray - on strike Dec 24 '10 at 07:42
  • Odd, I tried the exact same thing (along with IntPtr.Zero) but to no avail. What are you running this on? I have mine on windows 7, C# 3.5 Could you send me your code, maybe I mistyped something. Thanks – Jess Dec 24 '10 at 21:12
  • Hmm, I mistyped it, I used return new IntPtr(0); not 1, thanks! – Jess Dec 25 '10 at 01:00
  • @mazzzzz: Glad to see it worked for you! Indeed I'm using Server 2008 R2, so it should work just fine with Windows 7. – Cody Gray - on strike Dec 25 '10 at 02:44
  • 1
    You can do `CallNextHookEx` and then `return new IntPtr(1);`, so everybody gets the hook, but as stated [in docs](http://msdn.microsoft.com/en-us/library/windows/desktop/ms644988(v=vs.85).aspx) it will prevent message from propagating to the window. – Gman Feb 19 '13 at 18:16
3

A lot of touchpad drivers have this as an option. I.e. When you are typing, it ignores touchpad input. You could also turn off the tap-click, relying on the actual touchpad buttons to click.

First try the driver's configuration utility, before you try to write your own.

Tim
  • 8,912
  • 3
  • 39
  • 57