Skip to content

Code Quality: Improved WindowsStorables #17191

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions src/Files.App.CsWin32/ComPtr`1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,6 @@ public void Attach(T* other)
return (T**)Unsafe.AsPointer(ref Unsafe.AsRef(in this));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
[Obsolete("Use `HRESULT As<U>(U** other)` instead.")]
public readonly ComPtr<U> As<U>() where U : unmanaged, IComIID
{
ComPtr<U> ptr = default;
((IUnknown*)_ptr)->QueryInterface((Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in U.Guid)), (void**)ptr.GetAddressOf());
return ptr;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly HRESULT As<U>(U** other) where U : unmanaged, IComIID
{
Expand All @@ -91,22 +82,6 @@ public readonly HRESULT CoCreateInstance(Guid* rclsid, IUnknown* pUnkOuter = nul
return PInvoke.CoCreateInstance(rclsid, pUnkOuter, dwClsContext, (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in T.Guid)), (void**)this.GetAddressOf());
}

// Conversion operators

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator ComPtr<T>(T* other)
{
ComPtr<T> ptr = default;
ptr.Attach(other);
return ptr;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator T*(ComPtr<T> other)
{
return other._ptr;
}

// Disposer

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
6 changes: 6 additions & 0 deletions src/Files.App.CsWin32/ManualGuid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ public static Guid* IID_IStorageProviderStatusUISourceFactory

[GuidRVAGen.Guid("00021500-0000-0000-C000-000000000046")]
public static partial Guid* IID_IQueryInfo { get; }

[GuidRVAGen.Guid("BCC18B79-BA16-442F-80C4-8A59C30C463B")]
public static partial Guid* IID_IShellItemImageFactory { get; }

[GuidRVAGen.Guid("000214F9-0000-0000-C000-000000000046")]
public static partial Guid* IID_IShellLinkW { get; }
}

public static unsafe partial class CLSID
Expand Down
1 change: 1 addition & 0 deletions src/Files.App.CsWin32/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,4 @@ QITIPF_FLAGS
GetKeyboardState
MapVirtualKey
GetKeyboardLayout
S_FALSE
50 changes: 24 additions & 26 deletions src/Files.App.Storage/Storables/HomeFolder/HomeFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Files.App.Storage.Storables
{
public partial class HomeFolder : IHomeFolder
public unsafe partial class HomeFolder : IHomeFolder
{
public string Id => "Home"; // Will be "files://Home" in the future.

Expand Down Expand Up @@ -48,38 +48,36 @@ public IAsyncEnumerable<IStorableChild> GetQuickAccessFolderAsync(CancellationTo
/// <inheritdoc/>
public IAsyncEnumerable<IStorableChild> GetLogicalDrivesAsync(CancellationToken cancellationToken = default)
{
return GetLogicalDrives().ToAsyncEnumerable();
var availableDrives = PInvoke.GetLogicalDrives();
if (availableDrives is 0)
return Enumerable.Empty<IStorableChild>().ToAsyncEnumerable();

IEnumerable<IStorableChild> GetLogicalDrives()
{
var availableDrives = PInvoke.GetLogicalDrives();
if (availableDrives is 0)
yield break;

int count = BitOperations.PopCount(availableDrives);
var driveLetters = new char[count];
int count = BitOperations.PopCount(availableDrives);
var driveLetters = new char[count];

count = 0;
char driveLetter = 'A';
while (availableDrives is not 0)
{
if ((availableDrives & 1) is not 0)
driveLetters[count++] = driveLetter;
count = 0;
char driveLetter = 'A';
while (availableDrives is not 0)
{
if ((availableDrives & 1) is not 0)
driveLetters[count++] = driveLetter;

availableDrives >>= 1;
driveLetter++;
}
availableDrives >>= 1;
driveLetter++;
}

foreach (char letter in driveLetters)
{
cancellationToken.ThrowIfCancellationRequested();
List<IStorableChild> driveItems = [];
foreach (char letter in driveLetters)
{
cancellationToken.ThrowIfCancellationRequested();

if (WindowsStorable.TryParse($"{letter}:\\") is not IWindowsStorable driveRoot)
throw new InvalidOperationException();
if (WindowsStorable.TryParse($"{letter}:\\") is not IWindowsStorable driveRoot)
throw new InvalidOperationException();

yield return new WindowsFolder(driveRoot.ThisPtr);
}
driveItems.Add(new WindowsFolder(driveRoot.ThisPtr));
}

return driveItems.ToAsyncEnumerable();
}

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

namespace Files.App.Storage
{
public interface IWindowsFile : IWindowsStorable, IChildFile
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

namespace Files.App.Storage
{
public interface IWindowsFolder : IWindowsStorable, IChildFolder
{
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

using Windows.Win32;
using Windows.Win32.UI.Shell;

namespace Files.App.Storage
{
public interface IWindowsStorable : IDisposable
public unsafe interface IWindowsStorable : IStorableChild, IEquatable<IWindowsStorable>, IDisposable
{
ComPtr<IShellItem> ThisPtr { get; }
IShellItem* ThisPtr { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ namespace Files.App.Storage
/// <summary>
/// Handles bulk file operations in Windows, such as copy, move, delete, create, and rename, supporting progress tracking and event notifications.
/// </summary>
public sealed partial class WindowsBulkOperations : IDisposable
public unsafe partial class WindowsBulkOperations : IDisposable
{
// Fields

private readonly ComPtr<IFileOperation> _pFileOperation;
private readonly ComPtr<IFileOperationProgressSink> _pProgressSink;
private readonly IFileOperation* _pFileOperation;
private readonly IFileOperationProgressSink* _pProgressSink;
private readonly uint _progressSinkCookie;

// Events
Expand Down Expand Up @@ -70,24 +70,20 @@ public sealed partial class WindowsBulkOperations : IDisposable
/// <param name="flags">Defines the behavior of the file operation, such as allowing undo and suppressing directory confirmation.</param>
public unsafe WindowsBulkOperations(HWND ownerHWnd = default, FILEOPERATION_FLAGS flags = FILEOPERATION_FLAGS.FOF_ALLOWUNDO | FILEOPERATION_FLAGS.FOF_NOCONFIRMMKDIR)
{
var clsid = typeof(FileOperation).GUID;
var iid = typeof(IFileOperation).GUID;
IFileOperation* pFileOperation = null;

HRESULT hr = PInvoke.CoCreateInstance(
&clsid,
null,
CLSCTX.CLSCTX_LOCAL_SERVER,
&iid,
(void**)_pFileOperation.GetAddressOf())
.ThrowIfFailedOnDebug();
HRESULT hr = PInvoke.CoCreateInstance(CLSID.CLSID_FileOperation, null, CLSCTX.CLSCTX_LOCAL_SERVER, IID.IID_IFileOperation, (void**)&pFileOperation);
hr.ThrowIfFailedOnDebug();

_pFileOperation = pFileOperation;

if (ownerHWnd != default)
hr = _pFileOperation.Get()->SetOwnerWindow(ownerHWnd).ThrowIfFailedOnDebug();
hr = _pFileOperation->SetOwnerWindow(ownerHWnd).ThrowIfFailedOnDebug();

hr = _pFileOperation.Get()->SetOperationFlags(flags).ThrowIfFailedOnDebug();
hr = _pFileOperation->SetOperationFlags(flags).ThrowIfFailedOnDebug();

_pProgressSink.Attach((IFileOperationProgressSink*)WindowsBulkOperationsSink.Create(this));
hr = _pFileOperation.Get()->Advise(_pProgressSink.Get(), out var progressSinkCookie).ThrowIfFailedOnDebug();
_pProgressSink = (IFileOperationProgressSink*)WindowsBulkOperationsSink.Create(this);
hr = _pFileOperation->Advise(_pProgressSink, out var progressSinkCookie).ThrowIfFailedOnDebug();
_progressSinkCookie = progressSinkCookie;
}

Expand All @@ -101,7 +97,7 @@ public unsafe WindowsBulkOperations(HWND ownerHWnd = default, FILEOPERATION_FLAG
public unsafe HRESULT QueueCopyOperation(WindowsStorable targetItem, WindowsFolder destinationFolder, string? copyName)
{
fixed (char* pszCopyName = copyName)
return _pFileOperation.Get()->CopyItem(targetItem.ThisPtr.Get(), destinationFolder.ThisPtr.Get(), pszCopyName, _pProgressSink.Get());
return _pFileOperation->CopyItem(targetItem.ThisPtr, destinationFolder.ThisPtr, pszCopyName, _pProgressSink);
}

/// <summary>
Expand All @@ -111,7 +107,7 @@ public unsafe HRESULT QueueCopyOperation(WindowsStorable targetItem, WindowsFold
/// <returns>If this method succeeds, it returns <see cref="HRESULT.S_OK"/>. Otherwise, it returns an <see cref="HRESULT"/> error code.</returns>
public unsafe HRESULT QueueDeleteOperation(WindowsStorable targetItem)
{
return _pFileOperation.Get()->DeleteItem(targetItem.ThisPtr.Get(), _pProgressSink.Get());
return _pFileOperation->DeleteItem(targetItem.ThisPtr, _pProgressSink);
}

/// <summary>
Expand All @@ -124,7 +120,7 @@ public unsafe HRESULT QueueDeleteOperation(WindowsStorable targetItem)
public unsafe HRESULT QueueMoveOperation(WindowsStorable targetItem, WindowsFolder destinationFolder, string? newName)
{
fixed (char* pszNewName = newName)
return _pFileOperation.Get()->MoveItem(targetItem.ThisPtr.Get(), destinationFolder.ThisPtr.Get(), pszNewName, null);
return _pFileOperation->MoveItem(targetItem.ThisPtr, destinationFolder.ThisPtr, pszNewName, null);
}

/// <summary>
Expand All @@ -138,7 +134,7 @@ public unsafe HRESULT QueueMoveOperation(WindowsStorable targetItem, WindowsFold
public unsafe HRESULT QueueCreateOperation(WindowsFolder destinationFolder, FILE_FLAGS_AND_ATTRIBUTES fileAttributes, string name, string? templateName)
{
fixed (char* pszName = name, pszTemplateName = templateName)
return _pFileOperation.Get()->NewItem(destinationFolder.ThisPtr.Get(), (uint)fileAttributes, pszName, pszTemplateName, _pProgressSink.Get());
return _pFileOperation->NewItem(destinationFolder.ThisPtr, (uint)fileAttributes, pszName, pszTemplateName, _pProgressSink);
}

/// <summary>
Expand All @@ -150,7 +146,7 @@ public unsafe HRESULT QueueCreateOperation(WindowsFolder destinationFolder, FILE
public unsafe HRESULT QueueRenameOperation(WindowsStorable targetItem, string newName)
{
fixed (char* pszNewName = newName)
return _pFileOperation.Get()->RenameItem(targetItem.ThisPtr.Get(), pszNewName, _pProgressSink.Get());
return _pFileOperation->RenameItem(targetItem.ThisPtr, pszNewName, _pProgressSink);
}

/// <summary>
Expand All @@ -159,19 +155,19 @@ public unsafe HRESULT QueueRenameOperation(WindowsStorable targetItem, string ne
/// <returns>If this method succeeds, it returns <see cref="HRESULT.S_OK"/>. Otherwise, it returns an <see cref="HRESULT"/> error code.</returns>
public unsafe HRESULT PerformAllOperations()
{
return _pFileOperation.Get()->PerformOperations();
return _pFileOperation->PerformOperations();
}

// Disposer

/// <inheritdoc/>
public unsafe void Dispose()
{
if (!_pProgressSink.IsNull)
_pFileOperation.Get()->Unadvise(_progressSinkCookie);
if (_pProgressSink is not null)
_pFileOperation->Unadvise(_progressSinkCookie);

_pFileOperation.Dispose();
_pProgressSink.Dispose();
_pFileOperation->Release();
_pProgressSink->Release();
}
}
}
6 changes: 3 additions & 3 deletions src/Files.App.Storage/Storables/WindowsStorage/WindowsFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
namespace Files.App.Storage
{
[DebuggerDisplay("{" + nameof(ToString) + "()}")]
public sealed class WindowsFile : WindowsStorable, IChildFile
public unsafe class WindowsFile : WindowsStorable, IWindowsFile
{
public WindowsFile(ComPtr<IShellItem> nativeObject)
public WindowsFile(IShellItem* ptr)
{
ThisPtr = nativeObject;
ThisPtr = ptr;
}

public Task<Stream> OpenStreamAsync(FileAccess accessMode, CancellationToken cancellationToken = default)
Expand Down
Loading
Loading