From 65e1a733ce78332b4603a4f0b4a9a8905d46a673 Mon Sep 17 00:00:00 2001 From: PolarGoose <35307286+PolarGoose@users.noreply.github.com> Date: Tue, 10 Sep 2024 19:56:38 +0200 Subject: [PATCH] * Fix a potential failure during device discovery. * Improve error handling. Print more details in case of an unexpected error. --- src/Bluetooth/Devices/BluetoothLeDevice.cs | 3 ++- src/Bluetooth/Devices/DeviceDiscoverer.cs | 23 +++++++++++++++---- src/Bluetooth/Devices/DeviceInfoId.cs | 3 ++- src/Bluetooth/Devices/DevicePairer.cs | 9 ++++---- src/Bluetooth/Devices/DeviceUnPairer.cs | 5 ++-- src/Bluetooth/MacAddress.cs | 3 ++- src/BluetoothDevicePairing.csproj | 12 +++++----- .../DisconnectBluetoothAudioDeviceByName.cs | 3 ++- src/Commands/PairDeviceByName.cs | 5 ++-- src/Commands/UnPairDeviceByName.cs | 3 ++- src/Commands/Utils/AudioDeviceDisconnector.cs | 5 ++-- src/Commands/Utils/DeviceFinder.cs | 3 ++- src/Program.cs | 8 ++++++- src/Utils/AppException.cs | 10 ++++++++ 14 files changed, 67 insertions(+), 28 deletions(-) create mode 100644 src/Utils/AppException.cs diff --git a/src/Bluetooth/Devices/BluetoothLeDevice.cs b/src/Bluetooth/Devices/BluetoothLeDevice.cs index 704d7b6..1e6b52f 100644 --- a/src/Bluetooth/Devices/BluetoothLeDevice.cs +++ b/src/Bluetooth/Devices/BluetoothLeDevice.cs @@ -1,3 +1,4 @@ +using BluetoothDevicePairing.Utils; using System; namespace BluetoothDevicePairing.Bluetooth.Devices; @@ -21,7 +22,7 @@ public static BluetoothLeDevice FromMac(DeviceMacAddress mac) { var device = Windows.Devices.Bluetooth.BluetoothLEDevice.FromBluetoothAddressAsync(mac.RawAddress).GetAwaiter().GetResult(); return device == null - ? throw new Exception($"Can't create a BluetoothLE device from the provided mac address '{mac}'. Device with this mac address doesn't exist") + ? throw new AppException($"Can't create a BluetoothLE device from the provided mac address '{mac}'. Device with this mac address doesn't exist") : new BluetoothLeDevice(device); } } diff --git a/src/Bluetooth/Devices/DeviceDiscoverer.cs b/src/Bluetooth/Devices/DeviceDiscoverer.cs index 4bd9c62..79bc804 100644 --- a/src/Bluetooth/Devices/DeviceDiscoverer.cs +++ b/src/Bluetooth/Devices/DeviceDiscoverer.cs @@ -1,8 +1,11 @@ using BluetoothDevicePairing.Bluetooth.Devices.Utils; +using BluetoothDevicePairing.Utils; using System; using System.Collections.Generic; using System.Linq; using System.Threading; +using static Vanara.PInvoke.Ole32.PROPERTYKEY.System; +using static Vanara.PInvoke.User32; namespace BluetoothDevicePairing.Bluetooth.Devices; @@ -14,7 +17,7 @@ public DiscoveryTime(int timeInSeconds) { if (timeInSeconds is < 1 or > 30) { - throw new Exception($"discovery time should be in range [1; 30] but was {timeInSeconds}"); + throw new AppException($"discovery time should be in range [1; 30] but was {timeInSeconds}"); } Seconds = timeInSeconds; @@ -41,13 +44,23 @@ private static List Discover(AsqFilter filter, DiscoveryTime time) watcher.Start(); Thread.Sleep(time.Seconds * 1000); var devices = watcher.Stop(); - return devices.Select(CreateDevice).ToList(); + return devices.Select(CreateDevice).OfType().ToList(); } private static Device CreateDevice(Windows.Devices.Enumeration.DeviceInformation info) { - return new DeviceInfoId(info).DeviceType == DeviceType.Bluetooth - ? BluetoothDevice.FromDeviceInfo(info) - : BluetoothLeDevice.FromDeviceInfo(info); + try + { + // This can throw the following exception: + // System.ArgumentException: The parameter is incorrect. The provided device ID is not a valid BluetoothDevice object. + return new DeviceInfoId(info).DeviceType == DeviceType.Bluetooth + ? BluetoothDevice.FromDeviceInfo(info) + : BluetoothLeDevice.FromDeviceInfo(info); + } + catch (Exception ex) + { + Console.WriteLine($"Warning: failed to get information from the discovered device [{info.Name}; {info.Id}]. Error message: {ex.Message}"); + return null; + } } } diff --git a/src/Bluetooth/Devices/DeviceInfoId.cs b/src/Bluetooth/Devices/DeviceInfoId.cs index 21c1f16..8d66840 100644 --- a/src/Bluetooth/Devices/DeviceInfoId.cs +++ b/src/Bluetooth/Devices/DeviceInfoId.cs @@ -1,6 +1,7 @@ using System; using System.Text.RegularExpressions; using BluetoothDevicePairing.Bluetooth.Adapters; +using BluetoothDevicePairing.Utils; namespace BluetoothDevicePairing.Bluetooth.Devices; @@ -15,7 +16,7 @@ public DeviceInfoId(Windows.Devices.Enumeration.DeviceInformation info) var match = Regex.Match(info.Id, @"(^\w+)#(?Bluetooth|BluetoothLE)(?(..:){5}(..))-(?(..:){5}(..))$"); if (!match.Success) { - throw new Exception($"Failed to parse DeviceInformation.Id '{info.Id}'"); + throw new AppException($"Failed to parse DeviceInformation.Id '{info.Id}'"); } DeviceType = match.Groups["Type"].Value == "Bluetooth" ? DeviceType.Bluetooth : DeviceType.BluetoothLE; diff --git a/src/Bluetooth/Devices/DevicePairer.cs b/src/Bluetooth/Devices/DevicePairer.cs index 1e8f18b..b625ffd 100644 --- a/src/Bluetooth/Devices/DevicePairer.cs +++ b/src/Bluetooth/Devices/DevicePairer.cs @@ -1,3 +1,4 @@ +using BluetoothDevicePairing.Utils; using System; using System.Linq; @@ -11,14 +12,14 @@ public static void PairDevice(Device device, string pinCode) if (device.ConnectionStatus == ConnectionStatus.Connected) { - throw new Exception("Device is already connected, no need to pair"); + throw new AppException("Device is already connected, no need to pair"); } if (device.ConnectionStatus == ConnectionStatus.Paired) { if (!device.AssociatedAudioDevices.Any()) { - throw new Exception("Device is already paired"); + throw new AppException("Device is already paired"); } Console.WriteLine("Device is already paired. Connecting associated audio devices"); @@ -53,7 +54,7 @@ private static void Pair(Windows.Devices.Enumeration.DeviceInformationPairing pa .GetAwaiter().GetResult().Status; if (res != Windows.Devices.Enumeration.DevicePairingResultStatus.Paired) { - throw new Exception($"Failed to pair device. Status = {res}"); + throw new AppException($"Failed to pair device. Status = {res}"); } } @@ -82,7 +83,7 @@ private static void PairingRequestedHandler(Windows.Devices.Enumeration.DevicePa default: Console.WriteLine($"Unexpected pairing type: {args.PairingKind}"); - throw new Exception(); + throw new AppException(); } } } diff --git a/src/Bluetooth/Devices/DeviceUnPairer.cs b/src/Bluetooth/Devices/DeviceUnPairer.cs index 15be50b..53b05bf 100644 --- a/src/Bluetooth/Devices/DeviceUnPairer.cs +++ b/src/Bluetooth/Devices/DeviceUnPairer.cs @@ -1,3 +1,4 @@ +using BluetoothDevicePairing.Utils; using System; namespace BluetoothDevicePairing.Bluetooth.Devices; @@ -10,13 +11,13 @@ public static void UnpairDevice(Device device) if (device.ConnectionStatus == ConnectionStatus.NotPaired) { - throw new Exception("Device is not paired"); + throw new AppException("Device is not paired"); } var res = device.PairingInfo.UnpairAsync().GetAwaiter().GetResult().Status; if (res != Windows.Devices.Enumeration.DeviceUnpairingResultStatus.Unpaired) { - throw new Exception($"Failed to unpair the device. Status = {res}"); + throw new AppException($"Failed to unpair the device. Status = {res}"); } Console.WriteLine("Device has been successfully unpaired"); diff --git a/src/Bluetooth/MacAddress.cs b/src/Bluetooth/MacAddress.cs index 061d597..b0c7356 100644 --- a/src/Bluetooth/MacAddress.cs +++ b/src/Bluetooth/MacAddress.cs @@ -1,3 +1,4 @@ +using BluetoothDevicePairing.Utils; using System; using System.Linq; using System.Text.RegularExpressions; @@ -14,7 +15,7 @@ protected MacAddress(string mac) var match = Regex.Match(mac, @"^(..:){5}(..)$"); if (!match.Success) { - throw new Exception($"MacAddress address '{mac}' is not a valid mac address"); + throw new AppException($"MacAddress address '{mac}' is not a valid mac address"); } Address = mac; RawAddress = Convert.ToUInt64(Address.Replace(":", ""), 16); diff --git a/src/BluetoothDevicePairing.csproj b/src/BluetoothDevicePairing.csproj index 11fc3df..92e27af 100644 --- a/src/BluetoothDevicePairing.csproj +++ b/src/BluetoothDevicePairing.csproj @@ -7,21 +7,21 @@ Console utility to discover and pair Bluetooth and Bluetooth Low Energy devices. - + all - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + diff --git a/src/Commands/DisconnectBluetoothAudioDeviceByName.cs b/src/Commands/DisconnectBluetoothAudioDeviceByName.cs index 93e3cfa..9923790 100644 --- a/src/Commands/DisconnectBluetoothAudioDeviceByName.cs +++ b/src/Commands/DisconnectBluetoothAudioDeviceByName.cs @@ -1,5 +1,6 @@ using BluetoothDevicePairing.Bluetooth.Devices; using BluetoothDevicePairing.Commands.Utils; +using BluetoothDevicePairing.Utils; using CommandLine; using System; @@ -20,7 +21,7 @@ public static void Execute(DisconnectBluetoothAudioDeviceByNameOptions opts) opts.DeviceType); if (devices.Count > 1) { - throw new Exception($"{devices.Count} devices found, don't know which one to choose"); + throw new AppException($"{devices.Count} devices found, don't know which one to choose"); } AudioDeviceDisconnector.DisconnectBluetoothAudioDevice(devices[0]); diff --git a/src/Commands/PairDeviceByName.cs b/src/Commands/PairDeviceByName.cs index b8c70a6..6c081d1 100644 --- a/src/Commands/PairDeviceByName.cs +++ b/src/Commands/PairDeviceByName.cs @@ -1,5 +1,6 @@ using BluetoothDevicePairing.Bluetooth.Devices; using BluetoothDevicePairing.Commands.Utils; +using BluetoothDevicePairing.Utils; using CommandLine; using System; @@ -29,10 +30,10 @@ public static void Execute(PairDeviceByNameOptions opts) DevicePairer.PairDevice(devices[0], opts.PinCode); return; case 2: - throw new Exception( + throw new AppException( $"2 devices with the name '{opts.DeviceName}' found \n 1 - \"{devices[0]}\" \n 2 - \"{devices[1]}\". Don't know which one to choose."); default: - throw new Exception( + throw new AppException( $"{devices.Count} devices with the name '{opts.DeviceName}' found. Don't know which one to choose"); } } diff --git a/src/Commands/UnPairDeviceByName.cs b/src/Commands/UnPairDeviceByName.cs index 7ece7d5..1ff742d 100644 --- a/src/Commands/UnPairDeviceByName.cs +++ b/src/Commands/UnPairDeviceByName.cs @@ -1,5 +1,6 @@ using BluetoothDevicePairing.Bluetooth.Devices; using BluetoothDevicePairing.Commands.Utils; +using BluetoothDevicePairing.Utils; using CommandLine; using System; @@ -19,7 +20,7 @@ public static void Execute(UnpairDeviceByNameOptions opts) opts.DeviceType); if (devices.Count > 1) { - throw new Exception($"{devices.Count} devices found, don't know which one to choose"); + throw new AppException($"{devices.Count} devices found, don't know which one to choose"); } DeviceUnPairer.UnpairDevice(devices[0]); diff --git a/src/Commands/Utils/AudioDeviceDisconnector.cs b/src/Commands/Utils/AudioDeviceDisconnector.cs index 93dee89..29f843e 100644 --- a/src/Commands/Utils/AudioDeviceDisconnector.cs +++ b/src/Commands/Utils/AudioDeviceDisconnector.cs @@ -1,6 +1,7 @@ using BluetoothDevicePairing.Bluetooth.Devices; using System.Linq; using System; +using BluetoothDevicePairing.Utils; namespace BluetoothDevicePairing.Commands.Utils; @@ -12,12 +13,12 @@ public static void DisconnectBluetoothAudioDevice(Device device) if (device.ConnectionStatus != ConnectionStatus.Connected) { - throw new Exception($"The device '{device.Name}' is not connected"); + throw new AppException($"The device '{device.Name}' is not connected"); } if (!device.AssociatedAudioDevices.Any()) { - throw new Exception($"The device '{device.Name}' is not an audio device"); + throw new AppException($"The device '{device.Name}' is not an audio device"); } Console.WriteLine($"Disconnecting audio devices associated with '{device.Name}'"); diff --git a/src/Commands/Utils/DeviceFinder.cs b/src/Commands/Utils/DeviceFinder.cs index 146ecb0..95708be 100644 --- a/src/Commands/Utils/DeviceFinder.cs +++ b/src/Commands/Utils/DeviceFinder.cs @@ -1,4 +1,5 @@ using BluetoothDevicePairing.Bluetooth.Devices; +using BluetoothDevicePairing.Utils; using System; using System.Collections.Generic; @@ -10,7 +11,7 @@ public static List FindDevicesByName(List devices, string name, { var res = devices.FindAll(d => d.Name == name && deviceType == d.Id.DeviceType); return res.Count == 0 - ? throw new Exception($"Couldn't find any devices with '{name}' name and device type '{deviceType}'") + ? throw new AppException($"Couldn't find any devices with '{name}' name and device type '{deviceType}'") : res; } diff --git a/src/Program.cs b/src/Program.cs index 8736c9b..75d0b94 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -1,5 +1,6 @@ using System; using BluetoothDevicePairing.Commands; +using BluetoothDevicePairing.Utils; using CommandLine; static void ParseCommandLineAndExecuteActions(string[] args) @@ -27,8 +28,13 @@ static void ParseCommandLineAndExecuteActions(string[] args) ParseCommandLineAndExecuteActions(args); return 0; } -catch (Exception ex) +catch (AppException ex) { Console.WriteLine($"Failed: {ex.Message}"); return -1; } +catch (Exception ex) +{ + Console.WriteLine($"Unexpected failure: {ex}"); + return -1; +} diff --git a/src/Utils/AppException.cs b/src/Utils/AppException.cs new file mode 100644 index 0000000..5127522 --- /dev/null +++ b/src/Utils/AppException.cs @@ -0,0 +1,10 @@ +using System; + +namespace BluetoothDevicePairing.Utils; + +public sealed class AppException: ApplicationException +{ + public AppException() { } + + public AppException(string message): base(message) { } +}