Skip to content

Commit

Permalink
Merge PR #89
Browse files Browse the repository at this point in the history
Make YubiKeyDeviceListener resettable
  • Loading branch information
DennisDyallo authored Aug 19, 2024
2 parents e1dbe19 + debe19b commit 977e9c6
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 92 deletions.
72 changes: 72 additions & 0 deletions Yubico.Core/src/Yubico/Core/Devices/DeviceEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2024 Yubico AB
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;

namespace Yubico.Core.Devices
{
/// <summary>
/// Defines the contract for device-related event arguments in a generic context.
/// This interface allows for type-safe access to device information in event handling
/// scenarios, supporting device types that implement the <see cref="IDevice"/> interface.
/// It enables specific device type information to be preserved and accessed in event handlers.
/// </summary>
/// <remarks>
/// While this interface does not inherit from <see cref="System.EventArgs"/>, it retains the "Args" suffix
/// in its name. This naming convention is deliberately chosen to maintain consistency with standard
/// event argument naming patterns in C#, particularly for improved readability when used in delegate
/// and event handler signatures. The familiar "Args" suffix clearly indicates the interface's role
/// in event-related contexts, despite not directly extending <see cref="System.EventArgs"/>.
/// </remarks>
/// <typeparam name="TDevice">The specific type of <see cref="IDevice"/> this event argument represents.
/// This type parameter is covariant, allowing for more specific device types to be used
/// where a more general device type is expected.</typeparam>
#pragma warning disable CA1711 // Identifiers should not have incorrect suffix
public interface IDeviceEventArgs<out TDevice> where TDevice : IDevice
#pragma warning restore CA1711 // Identifiers should not have incorrect suffix
{
/// <summary>
/// Gets the specific type of <see cref="IDevice"/> that originated the event.
/// This property will always be populated, regardless of whether this is an arrival event or a removal event.
/// If the device was removed, not all members will be available on the object. An exception will be thrown if
/// you try to use the device in a way that requires it to be present.
/// </summary>
/// <remarks>
/// This property provides access to the specific <c>TDevice</c> instance associated with the current event.
/// </remarks>
/// <value>
/// An instance of <c>TDevice</c> that triggered this event.
/// </value>
TDevice Device { get; }
}

/// <summary>
/// Event arguments given whenever a device is added or removed from the system, providing strongly-typed access to the device that triggered the event.
/// </summary>
/// <typeparam name="TDevice">The type of device associated with this event, which must implement <see cref="IDevice"/>.</typeparam>
public abstract class DeviceEventArgs<TDevice> : EventArgs, IDeviceEventArgs<TDevice>
where TDevice : IDevice
{
/// <inheritdoc />
public TDevice Device { get; }

/// <summary>
/// Constructs a new instance of the <see cref="DeviceEventArgs{TDevice}"/> class.
/// </summary>
protected DeviceEventArgs(TDevice device)
{
Device = device;
}
}
}
16 changes: 2 additions & 14 deletions Yubico.Core/src/Yubico/Core/Devices/Hid/HidDeviceEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System;

namespace Yubico.Core.Devices.Hid
{
/// <summary>
/// Event arguments given whenever a HID device is added or removed from the system.
/// </summary>
public class HidDeviceEventArgs : EventArgs
public class HidDeviceEventArgs : DeviceEventArgs<IHidDevice>
{
/// <summary>
/// The HID device that originated the event.
/// </summary>
/// <remarks>
/// This property will always be populated, regardless of whether this is an arrival event or a removal event.
/// If the device was removed, not all members will be available on the object. An exception will be thrown if
/// you try to use the device in a way that requires it to be present.
/// </remarks>
public IHidDevice? Device { get; set; }

/// <summary>
/// Constructs a new instance of the <see cref="HidDeviceEventArgs"/> class.
/// </summary>
/// <param name="device">
/// The HID device that is originating this event.
/// </param>
public HidDeviceEventArgs(IHidDevice? device)
public HidDeviceEventArgs(IHidDevice device) : base(device)
{
Device = device;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ protected void OnArrived(IHidDevice device)
/// <param name="device">
/// The device instance that originates this event.
/// </param>
protected void OnRemoved(IHidDevice? device)
protected void OnRemoved(IHidDevice device)
{
_log.LogInformation("HID {Device} removed.", device);
Removed?.Invoke(this, new HidDeviceEventArgs(device));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,6 @@ private void ArrivedCallback(IntPtr context, int result, IntPtr sender, IntPtr d
OnArrived(new MacOSHidDevice(MacOSHidDevice.GetEntryId(device)));

private void RemovedCallback(IntPtr context, int result, IntPtr sender, IntPtr device) =>
OnRemoved(null);
OnRemoved(NullDevice.Instance);
}
}
38 changes: 38 additions & 0 deletions Yubico.Core/src/Yubico/Core/Devices/Hid/NullDevice.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2024 Yubico AB
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;

namespace Yubico.Core.Devices.Hid
{
public class NullDevice : IHidDevice
{
public string Path => string.Empty;
public string? ParentDeviceId => default;
public DateTime LastAccessed => default;
public short VendorId => default;
public short ProductId => default;
public short Usage => default;
public HidUsagePage UsagePage => default;
public IHidConnection ConnectToFeatureReports() => throw new NotImplementedException();
public IHidConnection ConnectToIOReports() => throw new NotImplementedException();

/// <summary>
/// Creates a default <see cref="IHidDevice"/> with all it's properties set to its default values, indicating a null device.
/// This might be used in cases where the <see cref="IHidDevice"/> otherwise would be null.
/// </summary>
internal static IHidDevice Instance => new NullDevice();
private NullDevice() { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ private static int OnEventReceived(IntPtr hNotify, IntPtr context, CM_NOTIFY_ACT
}
else if (action == CM_NOTIFY_ACTION.DEVICEINTERFACEREMOVAL)
{
thisObj?.OnRemoved(null);
thisObj?.OnRemoved(NullDevice.Instance);
}

return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System;

namespace Yubico.Core.Devices.SmartCard
{
/// <summary>
/// Event arguments given whenever a smart card device is added or removed from the system.
/// </summary>
public class SmartCardDeviceEventArgs : EventArgs
public class SmartCardDeviceEventArgs : DeviceEventArgs<ISmartCardDevice>
{
/// <summary>
/// The smart card device that originated the event.
/// </summary>
/// <remarks>
/// This property will always be populated, regardless of whether this is an arrival event or a removal event.
/// If the device was removed, not all members will be available on the object. An exception will be thrown if
/// you try to use the device in a way that requires it to be present.
/// </remarks>
public ISmartCardDevice Device { get; set; }

/// <summary>
/// Constructs a new instance of the <see cref="SmartCardDeviceEventArgs"/> class.
/// </summary>
/// <param name="device">
/// The smart card device that is originating this event.
/// </param>
public SmartCardDeviceEventArgs(ISmartCardDevice device)
public SmartCardDeviceEventArgs(ISmartCardDevice device) : base(device)
{
Device = device;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,10 @@ public void TestSpecializedKeyboardSupportsModhexString(KeyboardLayout layout)
#if Windows
#pragma warning disable CA1825
[Theory]
#pragma warning disable CA1825 // Avoid zero-length array allocations
// The compiler mistakenly thinks that this somehow involves a zero-length array.
[MemberData(nameof(GetTestData))]
#pragma warning restore CA1825 // Avoid zero-length array allocations
public void GetChar_GivenHidCode_ReturnsCorrectChar(KeyboardLayout layout, (char, byte)[] testData)
{
var hid = HidCodeTranslator.GetInstance(layout);
Expand Down
5 changes: 5 additions & 0 deletions Yubico.YubiKey/src/Yubico/YubiKey/YubiKeyDevice.Static.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ public partial class YubiKeyDevice
/// <see cref="IYubiKeyDevice"/> using their serial number. If they cannot be matched,
/// each connection will be returned as a separate <see cref="IYubiKeyDevice"/>.
/// </para>
/// <para>
/// If your application no longer needs to watch for insertion or removal notifications,
/// you can call <see cref="YubiKeyDeviceListener.StopListening"/> to release resources
/// and avoid the logging and other actions from the listeners.
/// </para>
/// </remarks>
/// <param name="transport">
/// Argument controls which devices are searched for. Values <see cref="Transport.None"/>
Expand Down
Loading

0 comments on commit 977e9c6

Please sign in to comment.