I am running an app where I would like single left button mouse clicks to be repeated while the left mouse button is held down (so basically removing the need to keep clicking). I wrote a small test app that hooks into the mouse events and listens for the lb down/up events. However, the app listens to mouse clicks anywhere on my desktop. Is there a way to make it listen to just a specific process?
Here is the test app I am using to listen for mouse clicks:
class Program
{
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
private static LowLevelMouseProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
private static bool _leftButtonDown;
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 static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 &&
MouseMessages.WM_LBUTTONUP == (MouseMessages)wParam)
{
_leftButtonDown = false;
Console.WriteLine(_leftButtonDown);
}
if (nCode >= 0 &&
MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
{
_leftButtonDown = true;
Console.WriteLine(_leftButtonDown);
StartTest();
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private static void StartTest()
{
Task.Factory.StartNew(() =>
{
while (_leftButtonDown)
{
Console.WriteLine("SENDING");
}
});
}
private const int WH_MOUSE_LL = 14;
private enum MouseMessages
{
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202
}
[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);
[DllImport("User32.dll")]
public static extern uint SendInput(uint numberOfInputs, [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] INPUT[] input, int structSize);
[StructLayout(LayoutKind.Sequential)]
public struct MOUSEINPUT
{
public int dx;
public int dy;
public uint mouseData;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct KEYBDINPUT
{
public ushort wVk;
public ushort wScan;
public uint dwFlags;
public uint time;
public IntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
public struct HARDWAREINPUT
{
uint uMsg;
ushort wParamL;
ushort wParamH;
}
[StructLayout(LayoutKind.Explicit)]
public struct INPUT
{
[FieldOffset(0)]
public int type;
[FieldOffset(4)] //*
public MOUSEINPUT mi;
[FieldOffset(4)] //*
public KEYBDINPUT ki;
[FieldOffset(4)] //*
public HARDWAREINPUT hi;
}
}
You can use the 4th argument of SetWindowsHookEx(), the dwThreadId argument, to be selective. You need to pass the thread ID of the GUI thread of the process you want to monitor. Obtain the value with, say, GetWindowThreadProcessId() if you have a handle of one of the windows owned by the process. If it is your own process then use GetCurrentThreadId(). If it is a Winforms app then favor IMessageFilter instead of a hook.
Related
I am currently developing an operating system using the MOSA-OS compiler to compile CIL to machine code. Unfortunately, while developing an operating system it is not so simple to just "Console.ReadLine" to get input. Nothing from System.Console is working, and I can't use WinForms either. Is there an alternative way to get keypresses/input?
Hooking into the user32.dll is a great way. Here is a low-level keyboard listener that will do the job. All you need to do is call HookKeyboard() and listen for the OnKeyPressed event.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Input;
namespace DesktopWPFAppLowLevelKeyboardHook
{
public class LowLevelKeyboardListener
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc 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);
public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
public event EventHandler<KeyPressedArgs> OnKeyPressed;
private LowLevelKeyboardProc _proc;
private IntPtr _hookID = IntPtr.Zero;
public LowLevelKeyboardListener()
{
_proc = HookCallback;
}
public void HookKeyboard()
{
_hookID = SetHook(_proc);
}
public void UnHookKeyboard()
{
UnhookWindowsHookEx(_hookID);
}
private IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
}
}
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
if (OnKeyPressed != null) { OnKeyPressed(this, new KeyPressedArgs(KeyInterop.KeyFromVirtualKey(vkCode))); }
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
}
public class KeyPressedArgs : EventArgs
{
public Key KeyPressed { get; private set; }
public KeyPressedArgs(Key key)
{
KeyPressed = key;
}
}
}
I've been having trouble with a certain problem. I've been disabling left and right mouse clicks for a particular application and my C# code looks like below...
My problem is that even though this works fine for disabling the mouse on a low level, isn't there a way in where I could disable it on a low level just for when the application is focused and therefor running?
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace GlobalLowLevelHooks
{
public class MouseHook
{
public bool enabled = false;
public delegate IntPtr MouseHookHandler(int nCode, IntPtr wParam, IntPtr lParam);
public MouseHookHandler hookHandler;
public delegate void MouseHookCallback(MSLLHOOKSTRUCT mouseStruct);
#region Events
public event MouseHookCallback LeftButtonDown;
public event MouseHookCallback LeftButtonUp;
public event MouseHookCallback RightButtonDown;
public event MouseHookCallback RightButtonUp;
public event MouseHookCallback MouseMove;
public event MouseHookCallback MouseWheel;
public event MouseHookCallback DoubleClick;
public event MouseHookCallback MiddleButtonDown;
public event MouseHookCallback MiddleButtonUp;
#endregion
public IntPtr hookID = IntPtr.Zero;
public void Enable()
{
hookHandler = HookFunc;
hookID = SetHook(hookHandler);
enabled = true;
}
public void Disable()
{
if (hookID == IntPtr.Zero)
return;
UnhookWindowsHookEx(hookID);
hookID = IntPtr.Zero;
enabled = false;
}
~MouseHook()
{
Enable();
}
private IntPtr SetHook(MouseHookHandler proc)
{
using (ProcessModule module = Process.GetCurrentProcess().MainModule)
return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(module.ModuleName), 0);
}
private IntPtr HookFunc(int nCode, IntPtr wParam, IntPtr lParam)
{
// parse system messages
if (nCode >= 0)
{
if (MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
{
return (IntPtr)1;
}
if (MouseMessages.WM_RBUTTONDOWN == (MouseMessages)wParam)
{
return (IntPtr)1;
}
if (MouseMessages.WM_MBUTTONDOWN == (MouseMessages)wParam)
{
return (IntPtr)1;
}
if (MouseMessages.WM_LBUTTONDBLCLK == (MouseMessages)wParam)
{
return (IntPtr)1;
}
}
return CallNextHookEx(hookID, nCode, wParam, lParam);
}
#region WinAPI
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,
WM_LBUTTONDBLCLK = 0x0203,
WM_MBUTTONDOWN = 0x0207,
WM_MBUTTONUP = 0x0208
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
public 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,
MouseHookHandler lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public 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);
#endregion
}
}
I'm trying to write a simple app that counts every mouse click that occurs in Windows (left and right separated), not only within a form. So far i did not have any success in finding such solution. Thanks!
You will need to use the Win32 Hook API to capture mouse events. Stephen Toub provides a nice bit of sample code on his MSDN blog post about setting up a mouse hook.
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);
}
In your code you would set this up to record WM_LBUTTONDOWN and WM_RBUTTONDOWN events.
While my C# WinForms app is running, I need to detect when the mouse pointer is moved over a 3rd party application.
I've done some Google'ing but I can't seem to find a useful code example that shows how to set up a Hook Procedure to another application using C# to detect Windows Messages.
Can someone please show me how to setup a C# hook procedure so I can detect the WM_MOUSEMOVE message on another app (e.g. Notepad.exe)?
Below is the C# Low Level Mouse Hook code I was looking for, from the blogs.msdn.com/b/toub/archive/2006/05/03/589468.aspx link (that David Heffernan posted)
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);
}
I have two pieces of code which are very similar in that they register global hooks.
Registering global keyboard hook:
public class KeyboardHook : IDisposable
{
#region Events
private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
public delegate void HookEventHandler(object sender, KeyboardHookEventArgs e);
public event HookEventHandler KeyDown;
public event HookEventHandler KeyUp;
#endregion
#region Constants
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
private LowLevelKeyboardProc _proc = null;
private static IntPtr _hookID = IntPtr.Zero;
#endregion
#region Imports
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc 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);
#endregion
#region Constructor
public KeyboardHook()
{
_proc = new LowLevelKeyboardProc(HookCallback);
_hookID = SetHook(_proc);
}
#endregion
#region Methods
private IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
//if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_SYSKEYDOWN))
{
int vkCode = Marshal.ReadInt32(lParam);
WinForms.Keys key = (WinForms.Keys)vkCode;
if (this.KeyDown != null)
this.KeyDown(this, new KeyboardHookEventArgs(vkCode));
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
#endregion
#region Destructor
public void Dispose()
{
UnhookWindowsHookEx(_hookID);
}
#endregion
}
Registering global shell hook:
public enum ShellEvents
{
HSHELL_WINDOWCREATED = 1,
HSHELL_WINDOWDESTROYED = 2,
HSHELL_ACTIVATESHELLWINDOW = 3,
HSHELL_WINDOWACTIVATED = 4,
HSHELL_GETMINRECT = 5,
HSHELL_REDRAW = 6,
HSHELL_TASKMAN = 7,
HSHELL_LANGUAGE = 8,
HSHELL_ACCESSIBILITYSTATE = 11
}
public class ShellHook
{
#region Events
private delegate IntPtr ShellProc(int nCode, IntPtr wParam, IntPtr lParam);
public delegate void HookEventHandler(object sender, ShellHookEventArgs e);
public event HookEventHandler WindowActivated;
#endregion
#region Constants
private const int WH_SHELL = 10;
private ShellProc _proc = null;
private static IntPtr _hookID = IntPtr.Zero;
#endregion
#region Imports
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
ShellProc 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);
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
#endregion
#region Fields
#endregion
#region Constructor
public ShellHook()
{
_proc = new ShellProc(HookCallback);
_hookID = SetHook(_proc);
}
#endregion
#region Methods
private IntPtr SetHook(ShellProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_SHELL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam.Equals(ShellEvents.HSHELL_WINDOWACTIVATED))
{
string windowTitle = GetWindowTitle(wParam);
if (this.WindowActivated != null)
this.WindowActivated(this, new ShellHookEventArgs(windowTitle));
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
private string GetWindowTitle(IntPtr hWnd)
{
const int nChars = 256;
StringBuilder Buff = new StringBuilder(nChars);
if (GetWindowText(hWnd, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
#endregion
#region Destructor
public void Dispose()
{
UnhookWindowsHookEx(_hookID);
}
#endregion
}
For some reason, the keyboard hook works, but the shell hook fails (SetWindowsHookEx returns 0 and the callback is never reached).
Any ideas why?
I think I'll answer my own question. This quote from pinvoke.net explains why...
However, you cannot implement global hooks in Microsoft .NET Framework
except low level hooks. To install a global hook, a hook must have a
native dynamic-link library (DLL) export to inject itself in another
process that requires a valid, consistent function to call into. This
requires a DLL export, which .NET Framework does not support. Managed
code has no concept of a consistent value for a function pointer
because these function pointers are proxies that are built
dynamically.