diff --git a/App/App.csproj b/App/App.csproj index c514219..59376d3 100644 --- a/App/App.csproj +++ b/App/App.csproj @@ -16,7 +16,7 @@ 10.0.22000.0 PerMonitorV2 AnyCPU;x64;ARM64;x86 - 2.1.4 + 2.1.5 https://github.com/soleon/Percentage https://github.com/soleon/Percentage?tab=MIT-1-ov-file Icon.png diff --git a/App/App.xaml.cs b/App/App.xaml.cs index 59fb44a..f8e0d0c 100644 --- a/App/App.xaml.cs +++ b/App/App.xaml.cs @@ -55,11 +55,19 @@ public App() ShutdownMode = ShutdownMode.OnExplicitShutdown; - DispatcherUnhandledException += (_, e) => HandleException(e.Exception); + DispatcherUnhandledException += (_, e) => + { + HandleException(e.Exception); + e.Handled = true; + }; AppDomain.CurrentDomain.UnhandledException += (_, e) => HandleException(e.ExceptionObject); - TaskScheduler.UnobservedTaskException += (_, e) => HandleException(e.Exception); + TaskScheduler.UnobservedTaskException += (_, e) => + { + HandleException(e.Exception); + e.SetObserved(); + }; InitializeComponent(); diff --git a/App/Extensions/DelegateExtensions.cs b/App/Extensions/DelegateExtensions.cs new file mode 100644 index 0000000..d7e4300 --- /dev/null +++ b/App/Extensions/DelegateExtensions.cs @@ -0,0 +1,24 @@ +using System; + +namespace Percentage.App.Extensions; + +internal static class DelegateExtensions +{ + private const int DefaultMaxRetryCount = 5; + + internal static void RetryOnException(this Action action, Action onRetryFailed = null, + int maxRetryCount = DefaultMaxRetryCount) where T : Exception + { + var lastRetry = maxRetryCount - 1; + for (var i = 0; i < maxRetryCount; i++) + try + { + action(); + return; + } + catch (T e) + { + if (i == lastRetry) onRetryFailed?.Invoke(e); + } + } +} \ No newline at end of file diff --git a/App/Extensions/NotifyIconExtensions.cs b/App/Extensions/NotifyIconExtensions.cs index 954bf6f..b804c54 100644 --- a/App/Extensions/NotifyIconExtensions.cs +++ b/App/Extensions/NotifyIconExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; using System.Windows; using System.Windows.Controls; using System.Windows.Media; @@ -11,16 +12,27 @@ internal static class NotifyIconExtensions { private const double DefaultNotifyIconSize = 16; + internal static void SetBatteryFullIcon(this NotifyIcon notifyIcon) + { + notifyIcon.SetIcon(new TextBlock + { + Text = "\uf5fc", + Foreground = BrushExtensions.GetBatteryNormalBrush(), + FontFamily = new FontFamily("Segoe Fluent Icons"), + FontSize = 16 + }); + } + internal static void SetIcon(this NotifyIcon notifyIcon, FrameworkElement textBlock) { // Measure the size of the element first. textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); - + // Use the desired size to work out the appropriate margin so that the element can be centre aligned in the // tray icon's 16-by-16 region. textBlock.Margin = new Thickness((DefaultNotifyIconSize - textBlock.DesiredSize.Width) / 2, (DefaultNotifyIconSize - textBlock.DesiredSize.Height) / 2, 0, 0); - + // Measure again for the correct desired size with the margin. textBlock.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); textBlock.Arrange(new Rect(textBlock.DesiredSize)); @@ -33,34 +45,15 @@ internal static void SetIcon(this NotifyIcon notifyIcon, FrameworkElement textBl dpiScale.PixelsPerInchX, dpiScale.PixelsPerInchY, PixelFormats.Default); - renderTargetBitmap.Render(textBlock); - // There's a chance that some native exception may be thrown when setting the icon's image. - // Catch any exception here and retry a few times then fail silently with logs. - for (var i = 0; i < 5; i++) - try - { - notifyIcon.Icon = renderTargetBitmap; - App.SetTrayIconUpdateError(null); - break; - } - catch (Exception e) - { - if (i == 4) - // Retried maximum number of times. - // Log error and continue. - App.SetTrayIconUpdateError(e); - } - } + // There's a chance that a COMException can be thrown when rendering the bitmap. + // Catch any COM exceptions here and retry a few times then fail silently. + DelegateExtensions.RetryOnException(() => renderTargetBitmap.Render(textBlock), + App.SetTrayIconUpdateError); - internal static void SetBatteryFullIcon(this NotifyIcon notifyIcon) - { - notifyIcon.SetIcon(new TextBlock - { - Text = "\uf5fc", - Foreground = BrushExtensions.GetBatteryNormalBrush(), - FontFamily = new FontFamily("Segoe Fluent Icons"), - FontSize = 16 - }); + // There's a chance that some native exception may be thrown when setting the icon's image. + // Catch any exception here and retry a few times then fail silently. + DelegateExtensions.RetryOnException(() => notifyIcon.Icon = renderTargetBitmap, + App.SetTrayIconUpdateError); } } \ No newline at end of file diff --git a/Pack/Package.appxmanifest b/Pack/Package.appxmanifest index 6853644..ac42f11 100644 --- a/Pack/Package.appxmanifest +++ b/Pack/Package.appxmanifest @@ -12,7 +12,7 @@ + Version="2.1.5.0" /> Battery Percentage Icon