From 8660bc5273202f4c440665d68f2ce23fea8e1e2f Mon Sep 17 00:00:00 2001 From: bcssov Date: Fri, 12 May 2023 02:28:59 +0200 Subject: [PATCH] Should fix #440 --- .../WindowsMountedVolumeInfoListener.cs | 179 ++++++++++++++++++ .../WindowsMountedVolumeInfoProvider.cs | 47 +++++ src/IronyModManager.Platform/Extensions.cs | 9 +- 3 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 src/IronyModManager.Platform/Drives/WindowsMountedVolumeInfoListener.cs create mode 100644 src/IronyModManager.Platform/Drives/WindowsMountedVolumeInfoProvider.cs diff --git a/src/IronyModManager.Platform/Drives/WindowsMountedVolumeInfoListener.cs b/src/IronyModManager.Platform/Drives/WindowsMountedVolumeInfoListener.cs new file mode 100644 index 00000000..58f531d9 --- /dev/null +++ b/src/IronyModManager.Platform/Drives/WindowsMountedVolumeInfoListener.cs @@ -0,0 +1,179 @@ +// *********************************************************************** +// Assembly : IronyModManager.Platform +// Author : Avalonia +// Created : 05-12-2023 +// +// Last Modified By : Mario +// Last Modified On : 05-12-2023 +// *********************************************************************** +// +// Avalonia +// +// Literally copied to fix a CTD as described here: https:github.com/AvaloniaUI/Avalonia/issues/8437. +// *********************************************************************** +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Reactive.Disposables; +using System.Reactive.Linq; +using Avalonia.Controls.Platform; +using Avalonia.Logging; + +namespace IronyModManager.Platform.Drives +{ + /// + /// Class WindowsMountedVolumeInfoListener. + /// Implements the + /// + /// + internal class WindowsMountedVolumeInfoListener : IDisposable + { + #region Fields + + /// + /// The disposables + /// + private readonly CompositeDisposable disposables; + + /// + /// The been disposed + /// + private bool beenDisposed = false; + + /// + /// The mounted drives + /// + private readonly ObservableCollection mountedDrives; + + #endregion Fields + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The mounted drives. + public WindowsMountedVolumeInfoListener(ObservableCollection mountedDrives) + { + this.mountedDrives = mountedDrives; + disposables = new CompositeDisposable(); + + var pollTimer = Observable.Interval(TimeSpan.FromSeconds(1)) + .Subscribe(Poll); + + disposables.Add(pollTimer); + + Poll(0); + } + + #endregion Constructors + + #region Methods + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (!beenDisposed) + { + if (disposing) + { + } + beenDisposed = true; + } + } + + /// + /// Polls the specified . + /// + /// The . + private void Poll(long _) + { + var allDrives = DriveInfo.GetDrives(); + + var mountVolInfos = allDrives + .Where(p => + { + try + { + var ret = p.IsReady; + return ret; + } + catch (Exception e) + { + Logger.TryGet(LogEventLevel.Warning, LogArea.Control)?.Log(this, $"Error in Windows drive enumeration: {e.Message}"); + } + return false; + }) + .Select(ParseDrive) + .Where(p => p.IsValid) + .ToArray(); + + if (mountedDrives.SequenceEqual(mountVolInfos)) + return; + else + { + mountedDrives.Clear(); + + foreach (var i in mountVolInfos) + mountedDrives.Add(i); + } + } + + /// + /// Parses the drive. + /// + /// The drive. + /// IronyModManager.Platform.Drives.SafeMountedVolumeInfo. + private SafeMountedVolumeInfo ParseDrive(DriveInfo drive) + { + try + { + return new SafeMountedVolumeInfo() + { + VolumeLabel = string.IsNullOrEmpty(drive.VolumeLabel.Trim()) ? drive.RootDirectory.FullName + : $"{drive.VolumeLabel} ({drive.Name})", + VolumePath = drive.RootDirectory.FullName, + VolumeSizeBytes = (ulong)drive.TotalSize, + IsValid = true + }; + } + catch (Exception ex) + { + Logger.TryGet(LogEventLevel.Warning, LogArea.Control)?.Log(this, $"Error in Windows drive enumeration, drive probably not ready: {ex.Message}"); + return new SafeMountedVolumeInfo() + { + IsValid = false + }; + } + } + + /// + /// Class SafeMountedVolumeInfo. + /// Implements the + /// + /// + private class SafeMountedVolumeInfo : MountedVolumeInfo + { + /// + /// Returns true if ... is valid. + /// + /// The is valid. + public bool IsValid { get; set; } + } + } + + #endregion Methods +} diff --git a/src/IronyModManager.Platform/Drives/WindowsMountedVolumeInfoProvider.cs b/src/IronyModManager.Platform/Drives/WindowsMountedVolumeInfoProvider.cs new file mode 100644 index 00000000..b328a5d3 --- /dev/null +++ b/src/IronyModManager.Platform/Drives/WindowsMountedVolumeInfoProvider.cs @@ -0,0 +1,47 @@ +// *********************************************************************** +// Assembly : IronyModManager.Platform +// Author : Avalonia +// Created : 05-12-2023 +// +// Last Modified By : Mario +// Last Modified On : 05-12-2023 +// *********************************************************************** +// +// Avalonia +// +// +// *********************************************************************** +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using Avalonia; +using Avalonia.Controls.Platform; + +namespace IronyModManager.Platform.Drives +{ + /// + /// Class WindowsMountedVolumeInfoProvider. + /// Implements the + /// + /// + public class WindowsMountedVolumeInfoProvider : IMountedVolumeInfoProvider + { + #region Methods + + /// + /// Listens to any changes in volume mounts and + /// forwards updates to the referenced + /// . + /// + /// The mounted drives. + /// IDisposable. + public IDisposable Listen(ObservableCollection mountedDrives) + { + Contract.Requires(mountedDrives != null); + return new WindowsMountedVolumeInfoListener(mountedDrives); + } + + #endregion Methods + } +} diff --git a/src/IronyModManager.Platform/Extensions.cs b/src/IronyModManager.Platform/Extensions.cs index 08bc643a..c7f39d22 100644 --- a/src/IronyModManager.Platform/Extensions.cs +++ b/src/IronyModManager.Platform/Extensions.cs @@ -4,7 +4,7 @@ // Created : 10-29-2020 // // Last Modified By : Mario -// Last Modified On : 11-24-2022 +// Last Modified On : 05-12-2023 // *********************************************************************** // // Mario @@ -15,6 +15,7 @@ using System.Collections.Generic; using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.Platform; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Platform; @@ -22,6 +23,7 @@ using IronyModManager.Platform.Assets; using IronyModManager.Platform.Clipboard; using IronyModManager.Platform.Configuration; +using IronyModManager.Platform.Drives; using IronyModManager.Platform.Fonts; using IronyModManager.Shared; @@ -91,6 +93,11 @@ public static TAppBuilder UseIronyPlatformDetect(this TAppBuilder b // Input manager var inputManager = AvaloniaLocator.Current.GetService(); AvaloniaLocator.CurrentMutable.Bind().ToConstant(new InputManager.InputManager(inputManager)); + // Windows only + if (os == OperatingSystemType.WinNT) + { + AvaloniaLocator.CurrentMutable.Bind().ToConstant(new WindowsMountedVolumeInfoProvider()); + } }); return builder; }