GetMouseMovePointsEx: Bounds / MOUSEMOVEPOINT in (mp_in) problems

陌路散爱 提交于 2019-12-02 17:14:52

问题


I'm trying to calculate cursor acceleration / velocity.

I read Find the velocity of the mouse in C# and decided to take Hans's suggestion of using GetMouseMovePointsEx (pinvoke.net, MSDN).

I made a demo program to test it out (see full code below) but it has a big limitation.

It won't return points once the cursor leaves the window.
In fact, the function returns -1 (win32Exception 1171, "The point passed to GetMouseMovePoints is not in the buffer") if execution isn't limited to points within the MainWindow.

  • I suspect this may be because I'm using Mouse.GetPosition() to provide the mp_in value.
    Could this be solved by using GetCursorPos possibly?

Moving or resizing the window causes myBounds.Contains(currentPosition) to be false when it should be true.

  • This is probably a dumb mistake related to how I am setting the bounds, but it's not immediately obvious to me why that is. It might also be connected to Mouse.GetPosition(). Whatever the reason, my comparisons aren't matching up. Please help?

What it looks like

MainWindow.xaml

<Window x:Class="MouseVelocity.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="450" Width="1200" Background="Gray"
    PreviewMouseUp="pMouseUp" PreviewMouseDown="pMouseDown">
<Grid Margin="1">
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="75"/>
    </Grid.RowDefinitions>
    <TextBlock x:Name="txtOutput" Background="LightGray"  Margin="10" FontSize="18" Padding="20"/>
    <Label x:Name="lbl_Velo" FontSize="22" FontWeight="Bold" Background="Black" Foreground="Red" 
           HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Grid.Row="1" Margin="10"/>
</Grid>

MainWindow.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using System.Windows.Interop;
using System.Runtime.InteropServices;

namespace MouseVelocity
{
    public partial class MainWindow : Window
    {
        DispatcherTimer GetMousePointsNow;
        MOUSEMOVEPOINT[] mp_out = new MOUSEMOVEPOINT[64];
        MOUSEMOVEPOINT LastMMP;
        Rect myBounds;
        double[] XVelocity;
        public const int GMMP_USE_DISPLAY_POINTS = 1;
        public const int GMMP_USE_HIGH_RESOLUTION_POINTS = 2;
        int nNumPointsDesired = 20;
        uint mode = GMMP_USE_DISPLAY_POINTS;
        string DataCollectionTime;
        int nVirtualWidth = GetSystemMetrics(SystemMetric.SM_CXVIRTUALSCREEN);
        int nVirtualHeight = GetSystemMetrics(SystemMetric.SM_CYVIRTUALSCREEN);
        int nVirtualLeft = GetSystemMetrics(SystemMetric.SM_XVIRTUALSCREEN);
        int nVirtualTop = GetSystemMetrics(SystemMetric.SM_YVIRTUALSCREEN);
        double ScreenWidth = System.Windows.SystemParameters.PrimaryScreenWidth;
        double ScreenHeight = System.Windows.SystemParameters.PrimaryScreenHeight;

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct MOUSEMOVEPOINT
        {
            public int x;
            public int y;
            public int time;
            public IntPtr dwExtraInfo;
        }

        [DllImport("user32.dll")]
        static extern int GetSystemMetrics(SystemMetric smIndex);

        [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern int GetMouseMovePointsEx(
                        uint cbSize,
                        [In] ref MOUSEMOVEPOINT pointsIn,
                        [Out] MOUSEMOVEPOINT[] pointsBufferOut,
                        int nBufPoints,
                        uint resolution
                        );

        public MainWindow()
        {
            InitializeComponent();

            GetMousePointsNow = new DispatcherTimer(new TimeSpan(0, 0, 0, 0, 20), DispatcherPriority.Background,
            GetMousePointsNow_Tick, Dispatcher.CurrentDispatcher); GetMousePointsNow.IsEnabled = false;

            myBounds = new Rect();
        }

        private void pMouseDown(object sender, MouseButtonEventArgs e) {
            GetMousePointsNow.Start(); }

        private void pMouseUp(object sender, MouseButtonEventArgs e) { 
            GetMousePointsNow.Stop(); }

        private void GetMousePointsNow_Tick(object sender, EventArgs e)
        {
            double width = System.Windows.SystemParameters.PrimaryScreenWidth;
            double height = System.Windows.SystemParameters.PrimaryScreenHeight;

            myBounds.Location = new Point(this.Top, this.Left);
            myBounds.Size = new Size(this.ActualWidth, this.ActualHeight);

            Point currentPosition = PointToScreen(Mouse.GetPosition(this));

            if (!myBounds.Contains(currentPosition)) {
                GetMousePointsNow.Stop(); return; }

            var mp_in = new MOUSEMOVEPOINT();
            mp_in.x = ((int)currentPosition.X) & 0x0000FFFF;
            mp_in.y = ((int)currentPosition.Y) & 0x0000FFFF;

            int cpt = GetMouseMovePointsEx((uint)(Marshal.SizeOf(mp_in)), ref mp_in, mp_out, nNumPointsDesired, mode);

            if (cpt == -1) {
                int win32Error = Marshal.GetLastWin32Error();
                throw new System.ComponentModel.Win32Exception(win32Error);
            }

            for (int i = 0; i < cpt; i++) {  // Fix for multi-display environment
                switch (mode) {
                    case GMMP_USE_DISPLAY_POINTS:
                        if (mp_out[i].x > 32767)
                            mp_out[i].x -= 65536;
                        if (mp_out[i].y > 32767)
                            mp_out[i].y -= 65536;
                        break;
                    case GMMP_USE_HIGH_RESOLUTION_POINTS:
                        mp_out[i].x = ((mp_out[i].x * (nVirtualWidth - 1)) - (nVirtualLeft * 65536)) / nVirtualWidth;
                        mp_out[i].y = ((mp_out[i].y * (nVirtualHeight - 1)) - (nVirtualTop * 65536)) / nVirtualHeight;
                        break;
                }
            }
            DisplayMousePoints();
            DisplayVelocity();
        }

        private void DisplayMousePoints()
        {
            string Result = ""; 
            XVelocity = new double[20];

            for (int i = 0; i < 20; i++)
            {
                MOUSEMOVEPOINT ThisMMP = mp_out[i];
                int DeltaTime = LastMMP.time - ThisMMP.time;

                if (ThisMMP.time == LastMMP.time) { } // Do nothing if same timestamp
                else {
                    XVelocity[i] = (LastMMP.x - ThisMMP.x) / (double)DeltaTime;  // V = x / t
                    if (DeltaTime > 0) // Don't include first point in the calculation
                    {
                        Result = Result += 
                            "X: " + mp_out[i].x + ",  " + 
                            "Timestamp: " + mp_out[i].time.ToString() + ",  " +
                            "Change: (" + DeltaTime.ToString() + "),  " +
                            "Point Velo: " + (XVelocity[i] * 100).ToString("0.000") + Environment.NewLine;
                    }
                }
                DataCollectionTime = DateTime.Now.Second.ToString("00") + DateTime.Now.Millisecond.ToString("000");
                txtOutput.Text = "Time Collected: " + DataCollectionTime + Environment.NewLine + Result;
                LastMMP = ThisMMP;
            }         
        }

        private void DisplayVelocity()
        {
            // Calculate a moving average of velocity values
            double aggregate = 0;
            double weight;
            int item = 1;
            int count = 1;

            foreach (var d in XVelocity)
            {
                weight = (double)item / (double)count;
                aggregate += (double)d * weight;
                count++;
            }
            double result = (aggregate / count) * 100;
            lbl_Velo.Content = result.ToString("0.000");      
        }

        public enum SystemMetric
        {
            SM_XVIRTUALSCREEN = 76, // 0x4C
            SM_YVIRTUALSCREEN = 77, // 0x4D
            SM_CXVIRTUALSCREEN = 78, // 0x4E
            SM_CYVIRTUALSCREEN = 79, // 0x4F
        } 
    }  
}

来源:https://stackoverflow.com/questions/25170813/getmousemovepointsex-bounds-mousemovepoint-in-mp-in-problems

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!