In order to receive RawInput messages, a thread needs to have a message handler. The simplest way to do this is to use a form, but that then means that when the flow of execution hits Application.Run(), execution halts, so your DLL constructor never returns. Eventually I worked out how to use a primitive version of a form, running in another thread, so that the code can be asynchronous.
So here it is: RawInput in AHK with the heavy lifting done in C# via the SharpDX library
FOR ALL DEMOS IN THIS THREAD:
The SharpDX DLLs from SharpDX.zip in this post are required for the demos in all posts in this thread
You will need CLR.ahk from here
For each demo, extract the SharpDX DLLs from this post, plus the MouseDelta zip for that post into the same folder
AHK code:
Code: Select all
#Persistent
#include clr.ahk
asm := CLR_LoadLibrary("MouseDelta.dll")
md := asm.CreateInstance("MouseDelta")
md.SubscribeRelativeMove(Func("MoveEvent"))
md.SubscribeWheel(Func("WheelEvent"))
return
MoveEvent(x, y){
Tooltip % x ", " y
}
WheelEvent(value){
Tooltip % "Wheel: " value
}
The procedure is always the same:
Create a new class library project, add a reference to SharpDX.RawInput via NuGet
Paste in the C# code
Compile
Code: Select all
using System;
using System.Windows.Forms;
using SharpDX.Multimedia;
using SharpDX.RawInput;
using System.Threading;
public class MouseDelta
{
private readonly Thread messagePump;
public dynamic relativeMoveCallback;
public dynamic wheelCallback;
private AutoResetEvent messagePumpRunning = new AutoResetEvent(false);
public MouseDelta()
{
// start message pump in its own thread
messagePump = new Thread(RunMessagePump) { Name = "ManualMessagePump" };
messagePump.Start();
messagePumpRunning.WaitOne();
}
public void SubscribeRelativeMove(dynamic callback)
{
relativeMoveCallback = callback;
}
public void SubscribeWheel(dynamic callback)
{
wheelCallback = callback;
}
// the message pump thread
private void RunMessagePump()
{
// Create control to handle windows messages
MessageHandler messageHandler = new MessageHandler();
// Register for RawInput mouse messages
Device.RegisterDevice(UsagePage.Generic, UsageId.GenericMouse, DeviceFlags.InputSink, messageHandler.Handle);
Device.MouseInput += ProcessMouseInput;
messagePumpRunning.Set();
Application.Run();
}
private void ProcessMouseInput(object sender, MouseInputEventArgs args)
{
//Console.WriteLine(string.Format("(x,y):({0},{1}) Buttons: {2} State: {3} Wheel: {4}\r\n", args.X, args.Y, args.ButtonFlags, args.Mode, args.WheelDelta));
if (args.Mode == MouseMode.MoveRelative && relativeMoveCallback != null && (Math.Abs(args.X) + Math.Abs(args.Y) > 0))
{
relativeMoveCallback(args.X, args.Y);
}
else if (args.WheelDelta != 0 && wheelCallback != null)
{
wheelCallback(args.WheelDelta / 120);
}
}
}
// Useful SO post on handling messages - code for overriding WndProc
// https://stackoverflow.com/questions/2443867/message-pump-in-net-windows-service
// Although the above code is not quite complete. This blog post has the implementation for MessageData
// http://joe-bq-wang.iteye.com/blog/1882661
// However, by overriding WndProc, we have to process all messages, and then you do not get a SharpDX object..
// ... you just appear to get a raw WM_INPUT message
// For now, this seems to serve our purposes
internal class MessageHandler : NativeWindow
{
public MessageHandler()
{
CreateHandle(new CreateParams());
}
}