0

我正在尝试计算光标加速度/速度。

我读了 Find the velocity in C#并决定采纳 Hans 的使用建议GetMouseMovePointsExpinvoke.netMSDN)。

我做了一个演示程序来测试它(见下面的完整代码),但它有一个很大的限制。

一旦光标离开窗口,它就不会返回点。
事实上,如果执行不限于 MainWindow 内的点,该函数会返回 -1(win32Exception 1171,“传递给 GetMouseMovePoints 的点不在缓冲区中”)。

  • 我怀疑这可能是因为我Mouse.GetPosition()用来提供mp_in价值。
    这可以通过使用GetCursorPos来解决吗?

myBounds.Contains(currentPosition)当它应该为真时,移动或调整窗口大小会导致为假。

  • 这可能是一个与我如何设置界限有关的愚蠢错误,但我并没有立即明白为什么会这样。它也可能连接到Mouse.GetPosition(). 不管是什么原因,我的比较不匹配。请帮忙?

它看起来像什么

主窗口.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>

主窗口.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
        } 
    }  
}
4

0 回答 0