切换RIDEV_CAPTUREMOUSE时出现奇怪的行为RIDEV_NOLEGACY

时间:2014-08-17 20:14:32

标签: c# .net windows winforms raw-input

我在C#中使用原始输入编写鼠标对象。设备注册并获取数据和所有内容,因此它在这方面工作。但是,在这个对象上我有一个名为" Exclusive"这意味着模仿直接输入中的独占模式。

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

现在问题是当我从鼠标按钮向下/向上事件执行此操作时。在我的鼠标对象上,我指定了鼠标按钮向下事件以将Exclusive设置为TRUE,在鼠标按下时我将其设置为FALSE。当我运行应用程序时,会触发事件并设置和重置独占模式。这就是奇怪的东西开始发生的地方:

  1. 在鼠标按下事件和禁用独占模式后,窗口不响应窗口装饰中的鼠标悬停事件(例如,关闭按钮不会突出显示,我可以&#39 ;点击它)。我也无法通过点击ALT + F4退出应用程序。但是,当我单击窗口一次或两次时,会返回常规窗口行为。

  2. 关闭应用程序后,Windows资源管理器和其他应用程序窗口会以相同的行为做出反应。我必须多次左键和右键单击才能让它们恢复正常。

  3. 很少有人因为某种奇怪的原因失去焦点。并且使用独占状态将所有内容抛入混乱状态(代码设置为在窗口取消激活时取消绑定设备,并在再次激活时恢复它)。正如我之前所说,这是一种非常罕见的情况,但仍然存在很大问题。

  4. 当我使用按键向下和按键事件设置/重置独占模式时,一切都运行正常,上述情况均未发生。这太令人困惑了。

    我在2台计算机上尝试过此代码,使用不同的鼠标,其中一台运行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();
    }
    

1 个答案:

答案 0 :(得分:1)

好吧,经过几天拉扯我留下的小头发,我找不到押韵或理由为什么会发生这种情况。所以,我设计了一个相当丑陋的黑客来伪造独家模式。

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

虽然这不是最优雅的解决方案,但它正如我想要的那样工作。但是,如果有人在使用NOLEGACY / CAPTUREMOUSE标志的同时找到更好的方法来处理这种情况,我很乐意将其标记为正确的答案。