Skip to content

Commit

Permalink
Init
Browse files Browse the repository at this point in the history
Revert "Init"

This reverts commit f238f71.

Init

Remove Vanara from DriveHelpers

Revert "Init"

This reverts commit 9cfee93.

Init

Update comments

Update
  • Loading branch information
0x5bfa committed Sep 24, 2024
1 parent 99b8408 commit 0232f7a
Show file tree
Hide file tree
Showing 29 changed files with 393 additions and 558 deletions.
1 change: 1 addition & 0 deletions src/Files.App.CsWin32/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,4 @@ IFileOperation
IShellItem2
PSGetPropertyKeyFromName
ShellExecuteEx
QueryDosDevice
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

namespace Files.App.Data.Items
namespace Files.App.Storage
{
public enum DeviceEvent
internal enum DeviceEvent
{
Added,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
// Licensed under the MIT License. See the LICENSE.

using Microsoft.Management.Infrastructure;
using System;

namespace Files.App.Data.EventArguments
namespace Files.App.Storage
{
/// <summary>
/// CimWatcher event args, which contains CimSubscriptionResult
Expand Down
2 changes: 2 additions & 0 deletions src/Files.App.Storage/Files.App.Storage.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

<ItemGroup>
<PackageReference Include="FluentFTP" Version="43.0.1" />
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" />
<PackageReference Include="Microsoft.Management.Infrastructure.Runtime.Win" Version="3.0.0" />
</ItemGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
Expand Down
151 changes: 151 additions & 0 deletions src/Files.App.Storage/Watchers/DeviceWatcher.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

using Microsoft.Management.Infrastructure;
using Windows.Devices.Enumeration;
using Windows.Devices.Portable;

namespace Files.App.Storage.Watchers
{
/// <inheritdoc cref="IDeviceWatcher"/>
public class DeviceWatcher : IDeviceWatcher
{
private Windows.Devices.Enumeration.DeviceWatcher? _winDeviceWatcher;

private ManagementEventWatcher? _mmiInsertWatcher, _mmiRemoveWatcher, _mmiModifyWatcher;

/// <inheritdoc/>
public bool CanBeStarted
=> _winDeviceWatcher?.Status is DeviceWatcherStatus.Created or DeviceWatcherStatus.Stopped or DeviceWatcherStatus.Aborted;

/// <inheritdoc/>
public event EventHandler<DeviceEventArgs>? DeviceAdded;

/// <inheritdoc/>
public event EventHandler<DeviceEventArgs>? DeviceChanged;

/// <inheritdoc/>
public event EventHandler<DeviceEventArgs>? DeviceDeleted;

/// <inheritdoc/>
public event EventHandler<DeviceEventArgs>? DeviceInserted;

/// <inheritdoc/>
public event EventHandler<DeviceEventArgs>? DeviceEjected;

/// <inheritdoc/>
public event EventHandler? EnumerationCompleted;

/// <summary>
/// Initializes an instance of <see cref="DeviceWatcher"/> class.
/// </summary>
public DeviceWatcher()
{
StartWatcher();
}

/// <inheritdoc/>
public void StartWatcher()
{
_winDeviceWatcher = DeviceInformation.CreateWatcher(StorageDevice.GetDeviceSelector());
_winDeviceWatcher.Added += Watcher_Added;
_winDeviceWatcher.Removed += Watcher_Removed;
_winDeviceWatcher.EnumerationCompleted += Watcher_EnumerationCompleted;

var insertQuery = "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_LogicalDisk'";
_mmiInsertWatcher = new(insertQuery);
_mmiInsertWatcher.EventArrived += new EventArrivedEventHandler(Device_Inserted);
_mmiInsertWatcher.Start();

var modifyQuery = "SELECT * FROM __InstanceModificationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_LogicalDisk' and TargetInstance.DriveType = 5";
_mmiModifyWatcher = new(modifyQuery);
_mmiModifyWatcher.EventArrived += new EventArrivedEventHandler(Device_Modified);
_mmiModifyWatcher.Start();

var removeQuery = "SELECT * FROM __InstanceDeletionEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_LogicalDisk'";
_mmiRemoveWatcher = new(removeQuery);
_mmiRemoveWatcher.EventArrived += new EventArrivedEventHandler(Device_Removed);
_mmiRemoveWatcher.Start();
}

/// <inheritdoc/>
public void StopWatcher()
{
if (_winDeviceWatcher?.Status is DeviceWatcherStatus.Started or DeviceWatcherStatus.EnumerationCompleted)
_winDeviceWatcher.Stop();

if (_winDeviceWatcher is not null)
{
_winDeviceWatcher.Added -= Watcher_Added;
_winDeviceWatcher.Removed -= Watcher_Removed;
_winDeviceWatcher.EnumerationCompleted -= Watcher_EnumerationCompleted;
}

_mmiInsertWatcher?.Dispose();
_mmiRemoveWatcher?.Dispose();
_mmiModifyWatcher?.Dispose();
_mmiInsertWatcher = null;
_mmiRemoveWatcher = null;
_mmiModifyWatcher = null;
}

private void Watcher_Added(Windows.Devices.Enumeration.DeviceWatcher sender, DeviceInformation args)
{
DeviceAdded?.Invoke(this, new(args.Name, args.Id));
}

private void Watcher_Removed(Windows.Devices.Enumeration.DeviceWatcher sender, DeviceInformationUpdate args)
{
DeviceDeleted?.Invoke(this, new(string.Empty, args.Id));
}

private void Watcher_EnumerationCompleted(Windows.Devices.Enumeration.DeviceWatcher sender, object args)
{
EnumerationCompleted?.Invoke(this, EventArgs.Empty);
}

private void Device_Modified(object sender, EventArrivedEventArgs e)
{
CimInstance obj = (CimInstance)e.NewEvent.Instance.CimInstanceProperties["TargetInstance"].Value;
var deviceName = obj.CimInstanceProperties["Name"]?.Value.ToString();
var deviceId = obj.CimInstanceProperties["DeviceID"]?.Value.ToString();
var volumeName = obj.CimInstanceProperties["VolumeName"]?.Value.ToString();
var eventType = volumeName is not null ? DeviceEvent.Inserted : DeviceEvent.Ejected;

Debug.WriteLine($"Drive modify event: {deviceName}, {deviceId}, {eventType}");

if (eventType is DeviceEvent.Inserted)
DeviceInserted?.Invoke(sender, new DeviceEventArgs(deviceName ?? string.Empty, deviceId ?? string.Empty));
else
DeviceEjected?.Invoke(sender, new DeviceEventArgs(deviceName ?? string.Empty, deviceId ?? string.Empty));
}

private void Device_Removed(object sender, EventArrivedEventArgs e)
{
CimInstance obj = (CimInstance)e.NewEvent.Instance.CimInstanceProperties["TargetInstance"].Value;
var deviceName = (string)obj.CimInstanceProperties["Name"].Value;
var deviceId = (string)obj.CimInstanceProperties["DeviceID"].Value;

Debug.WriteLine($"Drive removed event: {deviceName}, {deviceId}");

DeviceDeleted?.Invoke(sender, new DeviceEventArgs(deviceName, deviceId));
}

private void Device_Inserted(object sender, EventArrivedEventArgs e)
{
CimInstance obj = (CimInstance)e.NewEvent.Instance.CimInstanceProperties["TargetInstance"].Value;
var deviceName = (string)obj.CimInstanceProperties["Name"].Value;
var deviceId = (string)obj.CimInstanceProperties["DeviceID"].Value;

Debug.WriteLine($"Drive added event: {deviceName}, {deviceId}");

DeviceAdded?.Invoke(sender, new DeviceEventArgs(deviceName, deviceId));
}

/// <inheritdoc/>
public void Dispose()
{
StopWatcher();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,19 @@

using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Generic;
using System;
using System.Threading;

namespace Files.App.Helpers
namespace Files.App.Storage.Watchers
{
public delegate void EventArrivedEventHandler(object sender, EventArrivedEventArgs e);

/// <summary>
/// A public class used to start/stop the subscription to specific indication source,
/// and listen to the incoming indications, event <see cref="EventArrived" />
/// will be raised for each cimindication.
/// Original Sourced from: https://codereview.stackexchange.com/questions/255055/trying-to-replace-managementeventwatcher-class-in-system-management-to-switch-to
/// Adapted to newer versions of MMI
/// Watches to start/stop the subscription to specific indication source, and listen to the incoming indications.
/// </summary>
/// <remarks>
/// <see cref="EventArrived" /> will be raised for each CIM Indication.
/// <br/>
/// Credit: <a href="https://codereview.stackexchange.com/questions/255055/trying-to-replace-managementeventwatcher-class-in-system-management-to-switch-to">original source</a>.
/// </remarks>
public class ManagementEventWatcher : IDisposable, IObserver<CimSubscriptionResult>
{
internal enum CimWatcherStatus
Expand Down Expand Up @@ -46,15 +45,11 @@ internal enum CimWatcherStatus
/// <summary>
/// Initializes a new instance of the <see cref="ManagementEventWatcher" /> class.
/// </summary>
/// <param name="queryExpression"></param>
public ManagementEventWatcher(WqlEventQuery query)
/// <param name="query"></param>
public ManagementEventWatcher(string queryExpression)
{
string queryExpression = query.QueryExpression;

if (string.IsNullOrWhiteSpace(queryExpression))
{
throw new ArgumentNullException(nameof(queryExpression));
}

_nameSpace = DefaultNameSpace;
_queryDialect = DefaultQueryDialect;
Expand All @@ -71,9 +66,7 @@ public ManagementEventWatcher(WqlEventQuery query)
public ManagementEventWatcher(string queryDialect, string queryExpression)
{
if (string.IsNullOrWhiteSpace(queryExpression))
{
throw new ArgumentNullException(nameof(queryExpression));
}

_nameSpace = DefaultNameSpace;
_queryDialect = queryDialect ?? DefaultQueryDialect;
Expand All @@ -91,9 +84,7 @@ public ManagementEventWatcher(string queryDialect, string queryExpression)
public ManagementEventWatcher(string nameSpace, string queryDialect, string queryExpression)
{
if (string.IsNullOrWhiteSpace(queryExpression))
{
throw new ArgumentNullException(nameof(queryExpression));
}

_nameSpace = nameSpace ?? DefaultNameSpace;
_queryDialect = queryDialect ?? DefaultQueryDialect;
Expand All @@ -112,9 +103,7 @@ public ManagementEventWatcher(string nameSpace, string queryDialect, string quer
public ManagementEventWatcher(string computerName, string nameSpace, string queryDialect, string queryExpression)
{
if (string.IsNullOrWhiteSpace(queryExpression))
{
throw new ArgumentNullException(nameof(queryExpression));
}

_computerName = computerName;
_nameSpace = nameSpace ?? DefaultNameSpace;
Expand Down Expand Up @@ -161,14 +150,10 @@ public void Start()
lock (_myLock)
{
if (_isDisposed)
{
throw new ObjectDisposedException(nameof(ManagementEventWatcher));
}

if (_cimWatcherStatus != CimWatcherStatus.Default && _cimWatcherStatus != CimWatcherStatus.Stopped)
{
return;
}

_subscription = _cimObservable.Subscribe(this);

Expand All @@ -181,14 +166,10 @@ public void Stop()
lock (_myLock)
{
if (_isDisposed)
{
throw new ObjectDisposedException(nameof(ManagementEventWatcher));
}

if (_cimWatcherStatus != CimWatcherStatus.Started)
{
return;
}

_subscription?.Dispose();

Expand Down
7 changes: 2 additions & 5 deletions src/Files.App/Actions/FileSystem/FormatDriveAction.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

using Files.App.Utils.Shell;

namespace Files.App.Actions
{
internal sealed class FormatDriveAction : ObservableObject, IAction
{
private readonly IContentPageContext context;

private readonly DrivesViewModel drivesViewModel;
private readonly IRemovableDrivesService StorageDevicesService = Ioc.Default.GetRequiredService<IRemovableDrivesService>();

public string Label
=> "FormatDriveText".GetLocalizedResource();
Expand All @@ -20,13 +18,12 @@ public string Description
public bool IsExecutable =>
context.HasItem &&
!context.HasSelection &&
(drivesViewModel.Drives.Cast<DriveItem>().FirstOrDefault(x =>
(StorageDevicesService.Drives.Cast<DriveItem>().FirstOrDefault(x =>
string.Equals(x.Path, context.Folder?.ItemPath))?.MenuOptions.ShowFormatDrive ?? false);

public FormatDriveAction()
{
context = Ioc.Default.GetRequiredService<IContentPageContext>();
drivesViewModel = Ioc.Default.GetRequiredService<DrivesViewModel>();

context.PropertyChanged += Context_PropertyChanged;
}
Expand Down
27 changes: 8 additions & 19 deletions src/Files.App/Data/Contracts/IRemovableDrivesService.cs
Original file line number Diff line number Diff line change
@@ -1,38 +1,27 @@
// Copyright (c) 2024 Files Community
// Licensed under the MIT License. See the LICENSE.

using Files.Core.Storage.Storables;

namespace Files.App.Data.Contracts
{
/// <summary>
/// Represents a service to enumerate drives and create a storage device watcher
/// Represents a service to enumerate and update drives.
/// </summary>
public interface IRemovableDrivesService
public interface IRemovableDrivesService : INotifyPropertyChanged
{
/// <summary>
/// Gets the primary system drive. This item is typically excluded when enumerating removable drives
/// </summary>
/// <returns>The location of the drive which the operating system is installed to.</returns>
Task<ILocatableFolder> GetPrimaryDriveAsync();

/// <summary>
/// Creates a watcher for storage devices
/// Gets drives.
/// </summary>
/// <returns>The created storage device watcher</returns>
IStorageDeviceWatcher CreateWatcher();
ObservableCollection<ILocatableFolder> Drives { get; }

/// <summary>
/// Enumerates all removable drives
/// Gets or sets a value that indicates whether the application should show consent dialog when the primary drive isn't accessible.
/// </summary>
/// <returns>A collection of removable storage devices</returns>
IAsyncEnumerable<ILocatableFolder> GetDrivesAsync();
bool ShowUserConsentOnInit { get; set; }

/// <summary>
/// Refreshes the properties of a drive
/// Updates all connected devices.
/// </summary>
/// <param name="drive"></param>
/// <returns></returns>
Task UpdateDrivePropertiesAsync(ILocatableFolder drive);
Task UpdateDrivesAsync();
}
}
Loading

0 comments on commit 0232f7a

Please sign in to comment.