我正在C#中编写一个使用原始输入的鼠标对象。设备会注册并获取数据以及所有这些东西,因此它在这方面可以正常工作。但是,在此对象上,我有一个名为“Exclusive”的属性,该属性旨在模仿Direct Input中的独占模式。
当我将此属性切换为TRUE时,我将RAWINPUTDEVICE的dwFlags成员设置为RIRegister_CAPTUREMOUSE |来调用RegisterRawInputDevices。 RIDEV_NOLEGACY。当我将属性设置为FALSE时,我将其设置为0。
现在的问题是当我从鼠标按下/按下事件中执行此操作时。在我的鼠标对象上,我分配了鼠标按下事件以将Exclusive设置为TRUE,在鼠标按下时将其设置为FALSE。当我运行该应用程序时,将触发事件并设置并重置“独占”模式。这是奇怪的事情开始发生的地方:
当我使用按下键和按下键事件设置/重置“排他”模式时,所有操作均正常进行,而以上均未发生。这真是莫名其妙。
我已经在两台计算机上尝试了此代码,使用的鼠标不同,其中一台正在运行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/