Weird behaviour when changing RIDEV_CAPTUREMOUSE on register

Started by
1 comment, last by N0rF 9 years, 6 months ago

So I'm having the weirdest issue with Raw Input.

So I have a nice little wrapper class for Raw Input, and on that wrapper I have an "Exclusive" mode (kinda like what DirectInput did) property. This property calls RegisterRawInputDevices with the dwFlags member set to RIDEV_CAPTUREMOUSE | RIDEV_INPUTSINK | RIDEV_NOLEGACY when Exclusive is TRUE and to 0 when I set Exclusive to FALSE.

Now for the weirdness. If I try to set Exclusive to TRUE by right-clicking the mouse down and set Exclusive to FALSE on mouse up, it sometimes deactivates my window messes up the state since exclusive gets turned off when the window loses focus. This happens randomly. When it "works" and the mouse goes into exclusive mode and out of exclusive mode my window stops responding to mouse over events in the window decorations (e.g. the close button doesn't highlight), I can't press ALT+F4 to exit the application (although other key presses are intercepted with no problems), and I can't click on the close button until I left click on the window. Also, sometimes Windows explorer gets all messed up when this happens. For example, on the taskbar, no icons respond to mouse events and I have to right click a couple of times to get it responding again.

I've tried this code on two machines with Windows 7, and Windows 8.1 (x64 for both) and both exhibit the same behaviour. I've dug around on-line and I can't find anything related to this.

I considered that it might be due to calling RegisterRawInputDevices multiple times without unregistering the device. So, I unregistered by settings the dwFlags to RIDEV_REMOVE and setting the window handle to NULL before registering the device with the new dwFlags combination, but that didn't work.

Could it be due to the flags I'm using? Could it be due to calling RegisterRawInputDevices multiple times? Could this just be a quirk with Raw Input?

Here's the code that registers the device just for clarity (FYI, GetRawData just reads the data sent back by the WM_INPUT message, and does little else, so I did not include it):


		protected override void BindDevice()
		{
			BoundControl.MouseLeave -= Owner_MouseLeave;

			_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 BindException(Resources.RAW_CANNOT_UNBIND_POINTING_DEVICE);
		    }


			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) || (Exclusive))
		    {
		        _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 BindException(Resources.RAW_CANNOT_BIND_POINTING_DEVICE);
		    }

			if (!Exclusive)
			{
				OnWindowBound(BoundControl);
			}
		}

And this is how I process the WM_INPUT message, I register this message filter using Application.AddMessageFilter(MessageFilter);:


	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

Advertisement

I forgot to mention that if I change my Exclusive mode via a keyboard key press (KeyDown -> Exclusive = TRUE, KeyUp -> Exclusive = FALSE) then it works flawlessly. So that makes this even more weird.

Hi, I had similiar problem. I've discovered that if RegisterRawInputDevices is called with RIDEV_CAPTUREMOUSE | RIDEV_NOLEGACY flags when mouse button is pressed, problems would happen if application stops unexpectidely (crashed or stopped by breakpoint).

Just for me appropriate fix was to delay RegisterRawInputDevices before all mouse buttons would be unpressed.

This topic is closed to new replies.

Advertisement