我在C#中使用原始输入编写鼠标对象。设备注册并获取数据和所有内容,因此它在这方面工作。但是,在这个对象上我有一个名为" Exclusive"这意味着模仿直接输入中的独占模式。
当我将此属性切换为TRUE时,我调用RegisterRawInputDevices并将RAWINPUTDEVICE的dwFlags成员设置为:RIDEV_CAPTUREMOUSE | RIDEV_NOLEGACY。当我将属性设置为FALSE时,我将其设置为0。
现在问题是当我从鼠标按钮向下/向上事件执行此操作时。在我的鼠标对象上,我指定了鼠标按钮向下事件以将Exclusive设置为TRUE,在鼠标按下时我将其设置为FALSE。当我运行应用程序时,会触发事件并设置和重置独占模式。这就是奇怪的东西开始发生的地方:
在鼠标按下事件和禁用独占模式后,窗口不响应窗口装饰中的鼠标悬停事件(例如,关闭按钮不会突出显示,我可以&#39 ;点击它)。我也无法通过点击ALT + F4退出应用程序。但是,当我单击窗口一次或两次时,会返回常规窗口行为。
关闭应用程序后,Windows资源管理器和其他应用程序窗口会以相同的行为做出反应。我必须多次左键和右键单击才能让它们恢复正常。
很少有人因为某种奇怪的原因失去焦点。并且使用独占状态将所有内容抛入混乱状态(代码设置为在窗口取消激活时取消绑定设备,并在再次激活时恢复它)。正如我之前所说,这是一种非常罕见的情况,但仍然存在很大问题。
当我使用按键向下和按键事件设置/重置独占模式时,一切都运行正常,上述情况均未发生。这太令人困惑了。
我在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();
}
答案 0 :(得分:1)
好吧,经过几天拉扯我留下的小头发,我找不到押韵或理由为什么会发生这种情况。所以,我设计了一个相当丑陋的黑客来伪造独家模式。
首先,我从设备注册中删除了NOLEGACY和CAPTUREMOUSE标志,然后我将光标锁定在通过Cursor.Position接收输入的窗口的中心。然后我修改了我的窗口消息过滤器以丢弃窗口消息,如WM_MOUSEMOVE和WM_KEYDOWN,这样当设备处于独占模式时,它们不会被窗口拦截(除了处理ALT + F4的系统命令)。
虽然这不是最优雅的解决方案,但它正如我想要的那样工作。但是,如果有人在使用NOLEGACY / CAPTUREMOUSE标志的同时找到更好的方法来处理这种情况,我很乐意将其标记为正确的答案。