My code uses a WH_MOUSE_LL hook to initially suppress all mouse inputs unless a specific value is set for the dwExtraInfo property. The program is also registered for raw input for mouse devices, so that I can determine which device is responsible for the input. When I receive the WM_INPUT message and determine the source, depending on the device, I can just let the event take effect, in which case I recreate it using SendInput (also tried to supplant the mouse_event that was replaced) by providing data in the property dwExtraInfo. The idea is that the hook should see this new attached event, see additional information and not suppress it. Unfortunately, the injected event is never crocheted. The corresponding WM_INPUT message is visible by the window procedure, and SendInput returns 1, so it seems that the event is being generated. I read that the context switches to the thread that the hook set, and that it should have a message loop. I believe that my code corresponds to this qualification (since Application.Run () starts a message loop in a stream - I also tried writing a handwritten message outline in Win32 style). Does anyone have any idea why this is happening and / or how to fix it?
The following code demonstrates the problem. It should work with links to System and System.Windows.Forms.
Main class:
using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; using Win32; public class LLMH : Form { private static LowLevelMouseProc _proc = HookCallback; private static IntPtr _hookID = IntPtr.Zero; private InputProcessor inputProcessor; private static IntPtr extraInfoPointer; private const int EXTRA_INFO = 1000; public static void Main() { _hookID = SetHook(_proc); LLMH app = new LLMH(); app.inputProcessor = new InputProcessor(); app.RegisterDevice(0x01, 0x02, (int)RawInputDeviceFlag.RIDEV_INPUTSINK); int extraInfo = EXTRA_INFO; LLMH.extraInfoPointer = new IntPtr(extraInfo); Application.Run(); UnhookWindowsHookEx(_hookID); } public void RegisterDevice(ushort usagePage, ushort usage, int flags) { inputProcessor.RegisterDevice(usagePage, usage, flags, this.Handle); } protected override void WndProc(ref Message message) { switch (message.Msg) { case (int)WindowsMessage.WM_INPUT: Console.WriteLine("Received WM_INPUT."); RawInput rawInput = inputProcessor.GetRawInput(ref message); String name = inputProcessor.GetRawInputDeviceName(rawInput); Console.WriteLine("rawInput.extraInfo: {0}", rawInput.mouse.extraInformation); if (rawInput.mouse.extraInformation != EXTRA_INFO) { Console.WriteLine("Creating local mouse event.");
Class InputProcessor:
using System; using System.Runtime.InteropServices; using System.Windows.Forms; using Win32; public class InputProcessor { public RawInput GetRawInput(ref Message message) { uint size = 0; // Call with null pointer to get amount of memory required for buffer. GetRawInputData( message.LParam, (uint)RawInputDeviceCommand.RID_INPUT, IntPtr.Zero, ref size, (uint)Marshal.SizeOf(typeof(RawInputHeader))); IntPtr buffer = Marshal.AllocHGlobal((int)size); if (GetRawInputData( message.LParam, (uint)RawInputDeviceCommand.RID_INPUT, buffer, ref size, (uint)Marshal.SizeOf(typeof(RawInputHeader))) == size) { RawInput input = (RawInput)Marshal.PtrToStructure(buffer, typeof(RawInput)); Marshal.FreeHGlobal(buffer); return input; } else { throw new ApplicationException("Failed to return Raw Input"); } } public String GetRawInputDeviceName(RawInput rawInput) { uint length = 0; // Determine amount of memory to allocate for device name. GetRawInputDeviceInfo( rawInput.header.device, (uint)RawInputDeviceCommand.RIDI_DEVICENAME, IntPtr.Zero, ref length); String deviceName = String.Empty; if (length > 0) { IntPtr data = Marshal.AllocHGlobal((int)length); GetRawInputDeviceInfo(rawInput.header.device, (uint)RawInputDeviceCommand.RIDI_DEVICENAME, data, ref length); deviceName = (String)Marshal.PtrToStringAnsi(data); Marshal.FreeHGlobal(data); } return deviceName; } public void RegisterDevice(ushort usagePage, ushort usage, int flags, IntPtr windowHandle) { RawInputDevice[] inputDevices = new RawInputDevice[1]; inputDevices[0].usagePage = usagePage; inputDevices[0].usage = usage; inputDevices[0].flags = flags; inputDevices[0].windowHandle = windowHandle; if (!RegisterRawInputDevices(inputDevices, (uint)inputDevices.Length, (uint)Marshal.SizeOf(inputDevices[0]))) { throw new ApplicationException("Failed to register raw input devices."); } } [DllImport("user32.dll", SetLastError = true)] public static extern uint GetRawInputDeviceInfo( IntPtr deviceHandle, uint command, IntPtr data, ref uint size); [DllImport("User32.dll")] public static extern bool RegisterRawInputDevices( RawInputDevice[] rawInputDevice, uint numDevices, uint size); [DllImport("User32.dll")] public static extern int GetRawInputData( IntPtr rawInput, uint command, IntPtr data, ref uint size, uint headerSize); [DllImport("user32.dll")] public static extern uint GetRawInputDeviceList( IntPtr rawInputDeviceList, ref uint numDevices, uint size); }
Additional P / Invoke structures, enumerations, etc.
using System; using System.Runtime.InteropServices; namespace Win32 { public enum WindowsMessage { WM_NULL = 0x0000, WM_CREATE = 0x0001, WM_DESTROY = 0x0002, WM_MOVE = 0x0003, WM_SIZE = 0x0005, WM_ACTIVATE = 0x0006, WM_SETFOCUS = 0x0007, WM_KILLFOCUS = 0x0008, WM_QUIT = 0x0012, WM_INPUT = 0x00FF, WM_LBUTTONDOWN = 0x0201, WM_LBUTTONUP = 0x0202, WM_MOUSEMOVE = 0x0200, WM_MOUSEWHEEL = 0x020A, WM_RBUTTONDOWN = 0x0204, WM_RBUTTONUP = 0x0205 } public enum WindowsHook { WH_MOUSE_LL = 14 } [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; }
}
Program output after one WM_LBUTTONDOWN event:
In hook. Extra info doesn't match. hookStruct.dwExtraInfo: 0 Received WM_INPUT. rawInput.extraInfo: 0 Creating local mouse event. Return value of SendInput: 1 Received WM_INPUT. rawInput.extraInfo: 1000