Here is a minor issue I stumbled upon. Do with it as you see fit. ๐
I only encountered this because I don't need to observe mouse input.
Current Behavior
InputSource.Dispose()
throws NullReferenceException
if one of the passed constructor parameter values (IKeyboardEventReceiver
or IMouseEventReceiver
) is null.
This happens on a Windows 10 (build 19041.264) environment with both latest stable v2.0.0 NeatInput.Windows package from NuGet and latest development branch code. Let me know if more details are needed in case the issue cannot be reproduced.
Expected Behavior
InputSource.Dispose()
completes without throwing an exception if one of the constructor parameters is null. Or user/dev is informed earlier (during constructor call) about invalid parameters.
The constructor signature public InputSource(IKeyboardEventReceiver keyboardEventReceiver = null, IMouseEventReceiver mouseEventReceiver = null)
suggests that neither of the parameters are required due to the = null
optional parameter syntax. This conflicts with the fact that the constructor throws InvalidOperationException
when no parameters are passed to the constructor but that is a separate issue.
Workaround
Pass a dummy implementation of the unused event receiver to the InputSource
constructor.
Possible Solution(s)
- Improve
InputSource.Dispose()
to handle the null references. ๐ฆ <(O RLY?)
- Require passing valid event receiver instances to the
InputSource
constructor. E.g., remove the optional parameter syntax and throw ArgumentNullException
for null parameters.
- Internally replace the null constructor parameter values with dummy event receiver implementations.
Steps to Reproduce
.NET Core 3.1 console application that demonstrates current behaviour:
using System;
using NeatInput.Windows;
using NeatInput.Windows.Events;
namespace ReproduceException
{
class Program
{
static void Main(string[] args)
{
var inputSourceWithOnlyKeyboard = new InputSource(new KeyboardEventReceiver());
inputSourceWithOnlyKeyboard.Listen();
try
{
inputSourceWithOnlyKeyboard.Dispose();
Console.WriteLine($"{nameof(inputSourceWithOnlyKeyboard)} disposed as expected");
}
catch (NullReferenceException ex)
{
Console.WriteLine($"{nameof(inputSourceWithOnlyKeyboard)} threw {ex.GetType().Name} while disposing");
}
var inputSourceWithOnlyMouse = new InputSource(null, new MouseEventReceiver());
inputSourceWithOnlyMouse.Listen();
try
{
inputSourceWithOnlyMouse.Dispose();
Console.WriteLine($"{nameof(inputSourceWithOnlyMouse)} disposed as expected");
}
catch (NullReferenceException ex)
{
Console.WriteLine($"{nameof(inputSourceWithOnlyMouse)} threw {ex.GetType().Name} while disposing");
}
var inputSourceWithBothKeyboardAndMouse = new InputSource(new KeyboardEventReceiver(), new MouseEventReceiver());
inputSourceWithBothKeyboardAndMouse.Listen();
try
{
inputSourceWithBothKeyboardAndMouse.Dispose();
Console.WriteLine($"{nameof(inputSourceWithBothKeyboardAndMouse)} disposed as expected");
}
catch (NullReferenceException ex)
{
Console.WriteLine($"{nameof(inputSourceWithBothKeyboardAndMouse)} threw {ex.GetType().Name} while disposing");
}
Console.ReadKey();
}
}
public class KeyboardEventReceiver : IKeyboardEventReceiver
{
public void Receive(KeyboardEvent @event) { }
}
public class MouseEventReceiver : IMouseEventReceiver
{
public void Receive(MouseEvent @event) { }
}
}
Console output after running the example application:
inputSourceWithOnlyKeyboard threw NullReferenceException while disposing
inputSourceWithOnlyMouse threw NullReferenceException while disposing
inputSourceWithBothKeyboardAndMouse disposed as expected