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.
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With