0

I am using the example provided here: http://blogs.msdn.com/b/toub/archive/2006/05/03/589468.aspx

Together with my own code to calculate mouse delta.

Before anybody mentions it, I know there are ways in WPF and WF to directly access a mouse.delta property, but I need this to be a low level hook.

My modifications to the sample code are as follows:

private static Stopwatch CallbackTimestamp = new Stopwatch();
private static MSLLHOOKSTRUCT LastHook = new MSLLHOOKSTRUCT();
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{

    if (nCode >= 0 && MouseMessages.WM_MOUSEMOVE == (MouseMessages)wParam)
    {
        MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
        mouseExtensions.instance.InputChannels["X"].Value = hookStruct.pt.x;
        mouseExtensions.instance.InputChannels["Y"].Value = hookStruct.pt.y;

        int timeDif = (int)CallbackTimestamp.ElapsedTicks;

        if (timeDif > 0)
        {
            int xDif = hookStruct.pt.x - LastHook.pt.x;
            int yDif = hookStruct.pt.y - LastHook.pt.y;

            double xDelta = ((double)xDif / (double)timeDif) * 10000;

            mouseExtensions.instance.InputChannels["dX"].Value = (int)Math.Round(xDelta);
        }
        LastHook = hookStruct;
        CallbackTimestamp.Restart();
    }
    if (nCode >= 0 && MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
        mouseExtensions.instance.InputChannels["B1"].Value = true;

    if (nCode >= 0 && MouseMessages.WM_LBUTTONUP == (MouseMessages)wParam)
        mouseExtensions.instance.InputChannels["B1"].Value = false;

    if (nCode >= 0 && MouseMessages.WM_RBUTTONDOWN == (MouseMessages)wParam)
        mouseExtensions.instance.InputChannels["B2"].Value = true;

    if (nCode >= 0 && MouseMessages.WM_RBUTTONUP == (MouseMessages)wParam)
        mouseExtensions.instance.InputChannels["B2"].Value = false;

    return CallNextHookEx(_hookID, nCode, wParam, lParam);
}

It works pretty well for the most part, but the issue is I am getting occasional erratic outputs, when binding the value to a slider for instance, a consistent mouse movement will move the slider as expected except for sudden jolts to extreme min or max values. I thought perhaps it was a division by zero issue due to the callback being called in too rapid of secession for there to be any difference in the time span, but adding logic to ignore 0 does not seem to have made a difference.

I could store a list of values and use an average to help smooth out the errors, but Id rather fix the issue than put a band-aid on it.

Wobbles
  • 3,033
  • 1
  • 25
  • 51
  • Yes, the `timeDif` value is likely to be zero. Producing Infinity, and beyond. The clock doesn't tick that fast on Windows, 64 times per second by default. Maybe you ought to ignore that sample, maybe you ought to call timeBeginPeriod(), maybe you ought to do this completely differently. – Hans Passant Mar 24 '15 at 12:47
  • in the above example i account for the posibility of timeDif being zero, and stopwatch is much much more accurate than 64/s as far as I am aware. – Wobbles Mar 24 '15 at 12:55
  • I didn't look close enough to notice that you don't use the MSLLHOOKSTRUCT.time member. That's troublesome, Stopwatch only tells you when *your* code executes, it doesn't tell you when the mouse event was generated. – Hans Passant Mar 24 '15 at 12:59
  • I actually was originally using the time unit from the struct, but it was producing issues aswell, and the stopwatch.ticks seemed a more accurate unit of measurement, but I will likely switch back. – Wobbles Mar 24 '15 at 13:02
  • Switching back eliminates the the extreme errors, but the inaccuracy of the .time unit is itself causing errors, the value flickers around the true value too much. Looks like I may be forced to sample multiple reports and assign delta as an average. – Wobbles Mar 24 '15 at 13:06

0 Answers0