我正在C#中编写一个使用原始输入的鼠标对象。设备会注册并获取数据以及所有这些东西,因此它在这方面可以正常工作。但是,在此对象上,我有一个名为“Exclusive”的属性,该属性旨在模仿Direct Input中的独占模式。

当我将此属性切换为TRUE时,我将RAWINPUTDEVICE的dwFlags成员设置为RIRegister_CAPTUREMOUSE |来调用RegisterRawInputDevices。 RIDEV_NOLEGACY。当我将属性设置为FALSE时,我将其设置为0。

现在的问题是当我从鼠标按下/按下事件中执行此操作时。在我的鼠标对象上,我分配了鼠标按下事件以将Exclusive设置为TRUE,在鼠标按下时将其设置为FALSE。当我运行该应用程序时,将触发事件并设置并重置“独占”模式。这是奇怪的事情开始发生的地方:

  • 在将鼠标悬停事件和独占模式禁用后,窗口不会响应窗口装饰中的鼠标悬停事件(例如,关闭按钮不会突出显示,而我也无法单击它)。我也无法通过按ALT + F4退出应用程序。但是,当我单击窗口一次或两次时,常规的窗口行为又回来了。
  • 关闭应用程序后,Windows资源管理器和其他应用程序窗口将以相同的方式 react 。我必须左右单击多次才能使它们恢复正常。
  • 出于某些奇怪的原因,窗口很少会失去焦点。这会使一切都处于排他状态(将代码设置为在停用窗口时取消绑定(bind)设备,并在再次激活时恢复设备),从而使一切困惑。正如我之前所说,这是非常罕见的情况,但仍然很成问题。

  • 当我使用按下键和按下键事件设置/重置“排他”模式时,所有操作均正常进行,而以上均未发生。这真是莫名其妙。

    我已经在两台计算机上尝试了此代码,使用的鼠标不同,其中一台正在运行Windows 7 x64,另一台正在运行Windows 8.1 x64。

    在过去的几天里,我已经做了很多搜索工作,但是我空了出来,所以我想知道是否有人对为什么它会以这种方式有任何想法?我没有设置正确的标志吗?这样反复调用RegisterRawInputDevices会引起问题吗?

    这是我用来测试该问题的示例程序的代码:
    _mouse = _input.CreatePointingDevice(_form);
    _keyboard = _input.CreateKeyboard(_form);
    
    _mouse.PointingDeviceDown += (sender, args) =>
                                 {
                                     if ((args.Buttons & PointingDeviceButtons.Right) != PointingDeviceButtons.Right)
                                     {
                                         return;
                                     }
    
                                     _mouse.Exclusive = true;
                                 };
    
    _mouse.PointingDeviceMove += (sender, args) =>
                                 {
                                     _form.Text = string.Format("{0}x{1}", args.Position.X, args.Position.Y);
                                 };
    
    _mouse.PointingDeviceUp += (sender, args) =>
                               {
                                   if ((args.Buttons & PointingDeviceButtons.Right) != PointingDeviceButtons.Right)
                                   {
                                       return;
                                   }
    
                                   _mouse.CursorVisible = true;
                                   _mouse.Exclusive = false;
                               };
    

    这是我用来注册和注销鼠标的代码:
    /// <summary>
    /// Function to bind the input device.
    /// </summary>
    protected override void BindDevice()
    {
        BoundControl.MouseLeave -= Owner_MouseLeave;
    
        UnbindDevice();
    
        if (_messageFilter != null)
        {
            _messageFilter.RawInputPointingDeviceData -= GetRawData;
            _messageFilter.RawInputPointingDeviceData += GetRawData;
        }
    
        _device.UsagePage = HIDUsagePage.Generic;
        _device.Usage = (ushort)HIDUsage.Mouse;
        _device.Flags = RawInputDeviceFlags.None;
    
        // Enable background access.
        if (AllowBackground)
        {
            _device.Flags |= RawInputDeviceFlags.InputSink;
        }
    
        // Enable exclusive access.
        if (Exclusive)
        {
            _device.Flags |= RawInputDeviceFlags.CaptureMouse | RawInputDeviceFlags.NoLegacy;
        }
    
        _device.WindowHandle = BoundControl.Handle;
    
        // Attempt to register the device.
        if (!Win32API.RegisterRawInputDevices(_device))
        {
            throw new GorgonException(GorgonResult.DriverError, Resources.GORINP_RAW_CANNOT_BIND_POINTING_DEVICE);
        }
    
        if (!Exclusive)
        {
            OnWindowBound(BoundControl);
        }
    }
    
        /// <summary>
        /// Function to unbind the input device.
        /// </summary>
        protected override void UnbindDevice()
        {
            if (_messageFilter != null)
            {
                _messageFilter.RawInputPointingDeviceData -= GetRawData;
            }
    
            _device.UsagePage = HIDUsagePage.Generic;
            _device.Usage = (ushort)HIDUsage.Mouse;
            _device.Flags = RawInputDeviceFlags.Remove;
            _device.WindowHandle = IntPtr.Zero;
    
            // Attempt to register the device.
            if (!Win32API.RegisterRawInputDevices(_device))
            {
                throw new GorgonException(GorgonResult.DriverError, Resources.GORINP_RAW_CANNOT_UNBIND_POINTING_DEVICE);
            }
    
            BoundControl.MouseLeave -= Owner_MouseLeave;
        }
    

    这是处理WM_INPUT消息的代码:
    /// <summary>
    /// Object representing a message loop filter.
    /// </summary>
    internal class MessageFilter
        : System.Windows.Forms.IMessageFilter
    {
        #region Events.
        /// <summary>
        /// Event fired when a raw input keyboard event occours.
        /// </summary>
        public event EventHandler<RawInputKeyboardEventArgs> RawInputKeyboardData = null;
        /// <summary>
        /// Event fired when a pointing device event occurs.
        /// </summary>
        public event EventHandler<RawInputPointingDeviceEventArgs> RawInputPointingDeviceData = null;
        /// <summary>
        /// Event fired when an HID event occurs.
        /// </summary>
        public event EventHandler<RawInputHIDEventArgs> RawInputHIDData = null;
        #endregion
    
        #region Variables.
        private readonly int _headerSize = DirectAccess.SizeOf<RAWINPUTHEADER>();   // Size of the input data in bytes.
        #endregion
    
        #region IMessageFilter Members
        /// <summary>
        /// Filters out a message before it is dispatched.
        /// </summary>
        /// <param name="m">The message to be dispatched. You cannot modify this message.</param>
        /// <returns>
        /// true to filter the message and stop it from being dispatched; false to allow the message to continue to the next filter or control.
        /// </returns>
        public bool PreFilterMessage(ref System.Windows.Forms.Message m)
        {
            // Handle raw input messages.
            if ((WindowMessages)m.Msg != WindowMessages.RawInput)
            {
                return false;
            }
    
            unsafe
            {
                int dataSize = 0;
    
                // Get data size.
                int result = Win32API.GetRawInputData(m.LParam, RawInputCommand.Input, IntPtr.Zero, ref dataSize, _headerSize);
    
                if (result == -1)
                {
                    throw new GorgonException(GorgonResult.CannotRead, Resources.GORINP_RAW_CANNOT_READ_DATA);
                }
    
                // Get actual data.
                var rawInputPtr = stackalloc byte[dataSize];
                result = Win32API.GetRawInputData(m.LParam, RawInputCommand.Input, (IntPtr)rawInputPtr, ref dataSize, _headerSize);
    
                if ((result == -1) || (result != dataSize))
                {
                    throw new GorgonException(GorgonResult.CannotRead, Resources.GORINP_RAW_CANNOT_READ_DATA);
                }
    
                var rawInput = (RAWINPUT*)rawInputPtr;
    
                switch (rawInput->Header.Type)
                {
                    case RawInputType.Mouse:
                        if (RawInputPointingDeviceData != null)
                        {
                            RawInputPointingDeviceData(this,
                                                       new RawInputPointingDeviceEventArgs(rawInput->Header.Device, ref rawInput->Union.Mouse));
                        }
                        break;
                    case RawInputType.Keyboard:
                        if (RawInputKeyboardData != null)
                        {
                            RawInputKeyboardData(this, new RawInputKeyboardEventArgs(rawInput->Header.Device, ref rawInput->Union.Keyboard));
                        }
                        break;
                    default:
                        if (RawInputHIDData != null)
                        {
                            var HIDData = new byte[rawInput->Union.HID.Size * rawInput->Union.HID.Count];
                            var hidDataPtr = ((byte*)rawInput) + _headerSize + 8;
    
                            fixed (byte* buffer = &HIDData[0])
                            {
                                DirectAccess.MemoryCopy(buffer, hidDataPtr, HIDData.Length);
                            }
    
                            RawInputHIDData(this, new RawInputHIDEventArgs(rawInput->Header.Device, ref rawInput->Union.HID, HIDData));
                        }
                        break;
                }
            }
    
            return false;
        }
        #endregion
    }
    

    这是在处理WM_INPUT之后触发鼠标事件的代码:
    /// <summary>
    /// Function to retrieve and parse the raw pointing device data.
    /// </summary>
    /// <param name="sender">Sender of the event.</param>
    /// <param name="e">Event data to examine.</param>
    private void GetRawData(object sender, RawInputPointingDeviceEventArgs e)
    {
        if ((BoundControl == null) || (BoundControl.Disposing))
        {
            return;
        }
    
        if ((_deviceHandle != IntPtr.Zero) && (_deviceHandle != e.Handle))
        {
            return;
        }
    
        if ((Exclusive) && (!Acquired))
        {
            // Attempt to recapture.
            if (BoundControl.Focused)
            {
                Acquired = true;
            }
            else
            {
                return;
            }
        }
    
        // Do nothing if we're outside and we have exclusive mode turned off.
        if (!Exclusive)
        {
            if (!WindowRectangle.Contains(BoundControl.PointToClient(System.Windows.Forms.Cursor.Position)))
            {
                _outside = true;
                return;
            }
    
            if (_outside)
            {
                // If we're back inside place position at the entry point.
                _outside = false;
                Position = BoundControl.PointToClient(System.Windows.Forms.Cursor.Position);
            }
        }
    
        // Get wheel data.
        if ((e.PointingDeviceData.ButtonFlags & RawMouseButtons.MouseWheel) != 0)
        {
            OnPointingDeviceWheelMove((short)e.PointingDeviceData.ButtonData);
        }
    
        // If we're outside of the delay, then restart double click cycle.
        if (_doubleClicker.Milliseconds > DoubleClickDelay)
        {
            _doubleClicker.Reset();
            _clickCount = 0;
        }
    
        // Get button data.
        if ((e.PointingDeviceData.ButtonFlags & RawMouseButtons.LeftDown) != 0)
        {
            BeginDoubleClick(PointingDeviceButtons.Left);
            OnPointingDeviceDown(PointingDeviceButtons.Left);
        }
    
        if ((e.PointingDeviceData.ButtonFlags & RawMouseButtons.RightDown) != 0)
        {
            BeginDoubleClick(PointingDeviceButtons.Right);
            OnPointingDeviceDown(PointingDeviceButtons.Right);
        }
    
        if ((e.PointingDeviceData.ButtonFlags & RawMouseButtons.MiddleDown) != 0)
        {
            BeginDoubleClick(PointingDeviceButtons.Middle);
            OnPointingDeviceDown(PointingDeviceButtons.Middle);
        }
    
        if ((e.PointingDeviceData.ButtonFlags & RawMouseButtons.Button4Down) != 0)
        {
            BeginDoubleClick(PointingDeviceButtons.Button4);
            OnPointingDeviceDown(PointingDeviceButtons.Button4);
        }
    
        if ((e.PointingDeviceData.ButtonFlags & RawMouseButtons.Button5Down) != 0)
        {
            BeginDoubleClick(PointingDeviceButtons.Button5);
            OnPointingDeviceDown(PointingDeviceButtons.Button5);
        }
    
        // If we have an 'up' event on the buttons, remove the flag.
        if ((e.PointingDeviceData.ButtonFlags & RawMouseButtons.LeftUp) != 0)
        {
            if (IsDoubleClick(PointingDeviceButtons.Left))
            {
                _clickCount += 1;
            }
            else
            {
                _doubleClicker.Reset();
                _clickCount = 0;
            }
    
            OnPointingDeviceUp(PointingDeviceButtons.Left, _clickCount);
        }
    
        if ((e.PointingDeviceData.ButtonFlags & RawMouseButtons.RightUp) != 0)
        {
            if (IsDoubleClick(PointingDeviceButtons.Right))
            {
                _clickCount += 1;
            }
            else
            {
                _doubleClicker.Reset();
                _clickCount = 0;
            }
    
            OnPointingDeviceUp(PointingDeviceButtons.Right, _clickCount);
        }
    
        if ((e.PointingDeviceData.ButtonFlags & RawMouseButtons.MiddleUp) != 0)
        {
            if (IsDoubleClick(PointingDeviceButtons.Middle))
            {
                _clickCount += 1;
            }
            else
            {
                _doubleClicker.Reset();
                _clickCount = 0;
            }
    
            OnPointingDeviceUp(PointingDeviceButtons.Middle, _clickCount);
        }
    
        if ((e.PointingDeviceData.ButtonFlags & RawMouseButtons.Button4Up) != 0)
        {
            if (IsDoubleClick(PointingDeviceButtons.Button4))
            {
                _clickCount += 1;
            }
            else
            {
                _doubleClicker.Reset();
                _clickCount = 0;
            }
    
            OnPointingDeviceUp(PointingDeviceButtons.Button4, _clickCount);
        }
    
        if ((e.PointingDeviceData.ButtonFlags & RawMouseButtons.Button5Up) != 0)
        {
            if (IsDoubleClick(PointingDeviceButtons.Button5))
            {
                _clickCount += 1;
            }
            else
            {
                _doubleClicker.Reset();
                _clickCount = 0;
            }
    
            OnPointingDeviceUp(PointingDeviceButtons.Button5, _clickCount);
        }
    
        // Fire events.
        RelativePosition = new PointF(e.PointingDeviceData.LastX, e.PointingDeviceData.LastY);
        OnPointingDeviceMove(new PointF(Position.X + e.PointingDeviceData.LastX, Position.Y + e.PointingDeviceData.LastY), false);
        UpdateCursorPosition();
    }
    

    最佳答案

    好吧,在拉了几天我遗漏的小毛发之后,我找不到韵律或原因。因此,我设计了一个相当丑陋的骇客来假冒独占模式。

    首先,我从设备注册中删除了NOLEGACY和CAPTUREMOUSE标志,然后将光标锁定到通过Cursor.Position接收输入的窗口的中心。然后,我修改了我的窗口消息过滤器,以丢弃诸如WM_MOUSEMOVE和WM_KEYDOWN之类的窗口消息,以使它们在设备处于独占模式时不会被窗口拦截(处理ALT + F4的系统命令除外)。

    虽然这不是最优雅的解决方案,但它可以按照我的要求工作。但是,如果有人在仍然使用NOLEGACY/CAPTUREMOUSE标志的同时找到了一种更好的方法来处理这种情况,我将很高兴将其标记为正确的答案。

    关于c# - 切换RIDEV_CAPTUREMOUSE时的行为异常RIDEV_NOLEGACY,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25353236/

    10-10 02:13