diff --git a/src/Files.App.CsWin32/ComPtr`1.cs b/src/Files.App.CsWin32/ComPtr`1.cs index aafa936de894..6ceb19067cf8 100644 --- a/src/Files.App.CsWin32/ComPtr`1.cs +++ b/src/Files.App.CsWin32/ComPtr`1.cs @@ -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** other)` instead.")] - public readonly ComPtr As() where U : unmanaged, IComIID - { - ComPtr 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** other) where U : unmanaged, IComIID { @@ -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* other) - { - ComPtr ptr = default; - ptr.Attach(other); - return ptr; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator T*(ComPtr other) - { - return other._ptr; - } - // Disposer [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Files.App.CsWin32/ManualGuid.cs b/src/Files.App.CsWin32/ManualGuid.cs index 8ab59f21ee7f..521fe6b3d6a4 100644 --- a/src/Files.App.CsWin32/ManualGuid.cs +++ b/src/Files.App.CsWin32/ManualGuid.cs @@ -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 diff --git a/src/Files.App.CsWin32/NativeMethods.txt b/src/Files.App.CsWin32/NativeMethods.txt index ce5524f6885e..fe13428f12a4 100644 --- a/src/Files.App.CsWin32/NativeMethods.txt +++ b/src/Files.App.CsWin32/NativeMethods.txt @@ -225,3 +225,4 @@ QITIPF_FLAGS GetKeyboardState MapVirtualKey GetKeyboardLayout +S_FALSE diff --git a/src/Files.App.Storage/Storables/HomeFolder/HomeFolder.cs b/src/Files.App.Storage/Storables/HomeFolder/HomeFolder.cs index 790cbc17c13e..ae976667d582 100644 --- a/src/Files.App.Storage/Storables/HomeFolder/HomeFolder.cs +++ b/src/Files.App.Storage/Storables/HomeFolder/HomeFolder.cs @@ -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. @@ -48,38 +48,36 @@ public IAsyncEnumerable GetQuickAccessFolderAsync(CancellationTo /// public IAsyncEnumerable GetLogicalDrivesAsync(CancellationToken cancellationToken = default) { - return GetLogicalDrives().ToAsyncEnumerable(); + var availableDrives = PInvoke.GetLogicalDrives(); + if (availableDrives is 0) + return Enumerable.Empty().ToAsyncEnumerable(); - IEnumerable 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 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(); } /// diff --git a/src/Files.App.Storage/Storables/WindowsStorage/IWindowsFile.cs b/src/Files.App.Storage/Storables/WindowsStorage/IWindowsFile.cs new file mode 100644 index 000000000000..43f30155f907 --- /dev/null +++ b/src/Files.App.Storage/Storables/WindowsStorage/IWindowsFile.cs @@ -0,0 +1,9 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +namespace Files.App.Storage +{ + public interface IWindowsFile : IWindowsStorable, IChildFile + { + } +} diff --git a/src/Files.App.Storage/Storables/WindowsStorage/IWindowsFolder.cs b/src/Files.App.Storage/Storables/WindowsStorage/IWindowsFolder.cs new file mode 100644 index 000000000000..540ccdf19600 --- /dev/null +++ b/src/Files.App.Storage/Storables/WindowsStorage/IWindowsFolder.cs @@ -0,0 +1,15 @@ +// Copyright (c) Files Community +// Licensed under the MIT License. + +using Windows.Win32.UI.Shell; + +namespace Files.App.Storage +{ + public unsafe interface IWindowsFolder : IWindowsStorable, IChildFolder + { + /// + /// Gets or sets the cached for the ShellNew context menu. + /// + public IContextMenu* ShellNewMenu { get; } + } +} diff --git a/src/Files.App.Storage/Storables/WindowsStorage/IWindowsStorable.cs b/src/Files.App.Storage/Storables/WindowsStorage/IWindowsStorable.cs index 421d7a68dddd..1cd69e32a44c 100644 --- a/src/Files.App.Storage/Storables/WindowsStorage/IWindowsStorable.cs +++ b/src/Files.App.Storage/Storables/WindowsStorage/IWindowsStorable.cs @@ -1,13 +1,14 @@ // 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, IDisposable { - ComPtr ThisPtr { get; } + IShellItem* ThisPtr { get; } + + IContextMenu* ContextMenu { get; } } } diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsBulkOperations.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsBulkOperations.cs index a6393243246f..d995d20ea71b 100644 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsBulkOperations.cs +++ b/src/Files.App.Storage/Storables/WindowsStorage/WindowsBulkOperations.cs @@ -12,12 +12,12 @@ namespace Files.App.Storage /// /// Handles bulk file operations in Windows, such as copy, move, delete, create, and rename, supporting progress tracking and event notifications. /// - public sealed partial class WindowsBulkOperations : IDisposable + public unsafe partial class WindowsBulkOperations : IDisposable { // Fields - private readonly ComPtr _pFileOperation; - private readonly ComPtr _pProgressSink; + private readonly IFileOperation* _pFileOperation; + private readonly IFileOperationProgressSink* _pProgressSink; private readonly uint _progressSinkCookie; // Events @@ -70,24 +70,20 @@ public sealed partial class WindowsBulkOperations : IDisposable /// Defines the behavior of the file operation, such as allowing undo and suppressing directory confirmation. 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; } @@ -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); } /// @@ -111,7 +107,7 @@ public unsafe HRESULT QueueCopyOperation(WindowsStorable targetItem, WindowsFold /// If this method succeeds, it returns . Otherwise, it returns an error code. public unsafe HRESULT QueueDeleteOperation(WindowsStorable targetItem) { - return _pFileOperation.Get()->DeleteItem(targetItem.ThisPtr.Get(), _pProgressSink.Get()); + return _pFileOperation->DeleteItem(targetItem.ThisPtr, _pProgressSink); } /// @@ -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); } /// @@ -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); } /// @@ -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); } /// @@ -159,7 +155,7 @@ public unsafe HRESULT QueueRenameOperation(WindowsStorable targetItem, string ne /// If this method succeeds, it returns . Otherwise, it returns an error code. public unsafe HRESULT PerformAllOperations() { - return _pFileOperation.Get()->PerformOperations(); + return _pFileOperation->PerformOperations(); } // Disposer @@ -167,11 +163,11 @@ public unsafe HRESULT PerformAllOperations() /// 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(); } } } diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsFile.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsFile.cs index 3ce56f786c2f..93362f26a7e9 100644 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsFile.cs +++ b/src/Files.App.Storage/Storables/WindowsStorage/WindowsFile.cs @@ -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 nativeObject) + public WindowsFile(IShellItem* ptr) { - ThisPtr = nativeObject; + ThisPtr = ptr; } public Task OpenStreamAsync(FileAccess accessMode, CancellationToken cancellationToken = default) diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsFolder.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsFolder.cs index f4105687184f..ee30f9eebe6f 100644 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsFolder.cs +++ b/src/Files.App.Storage/Storables/WindowsStorage/WindowsFolder.cs @@ -10,80 +10,76 @@ namespace Files.App.Storage { [DebuggerDisplay("{" + nameof(ToString) + "()}")] - public sealed class WindowsFolder : WindowsStorable, IChildFolder + public unsafe class WindowsFolder : WindowsStorable, IWindowsFolder { - public WindowsFolder(ComPtr nativeObject) + /// + public IContextMenu* ShellNewMenu { - ThisPtr = nativeObject; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected internal set; } - public unsafe WindowsFolder(IShellItem* nativeObject) + public WindowsFolder(IShellItem* ptr) { - ComPtr ptr = default; - ptr.Attach(nativeObject); ThisPtr = ptr; } - public unsafe WindowsFolder(Guid folderId) + public WindowsFolder(Guid folderId) { - ComPtr pItem = default; + IShellItem* pShellItem = default; - HRESULT hr = PInvoke.SHGetKnownFolderItem(&folderId, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, IID.IID_IShellItem, (void**)pItem.GetAddressOf()); + HRESULT hr = PInvoke.SHGetKnownFolderItem(&folderId, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, IID.IID_IShellItem, (void**)&pShellItem); if (hr.Failed) { fixed (char* pszShellPath = $"Shell:::{folderId:B}") - hr = PInvoke.SHCreateItemFromParsingName(pszShellPath, null, IID.IID_IShellItem, (void**)pItem.GetAddressOf()); + hr = PInvoke.SHCreateItemFromParsingName(pszShellPath, null, IID.IID_IShellItem, (void**)&pShellItem); // Invalid FOLDERID; this should never happen. hr.ThrowOnFailure(); } - ThisPtr = pItem; + ThisPtr = pShellItem; } public IAsyncEnumerable GetItemsAsync(StorableType type = StorableType.All, CancellationToken cancellationToken = default) { - return GetItems().ToAsyncEnumerable(); + using ComPtr pEnumShellItems = default; - unsafe IEnumerable GetItems() - { - ComPtr pEnumShellItems = default; - GetEnumerator(); + HRESULT hr = ThisPtr->BindToHandler(null, BHID.BHID_EnumItems, IID.IID_IEnumShellItems, (void**)pEnumShellItems.GetAddressOf()); + if (hr.ThrowIfFailedOnDebug().Failed) + return Enumerable.Empty().ToAsyncEnumerable(); - ComPtr pShellItem = default; - while (GetNext() && !pShellItem.IsNull) - { - cancellationToken.ThrowIfCancellationRequested(); - var isFolder = pShellItem.HasShellAttributes(SFGAO_FLAGS.SFGAO_FOLDER); - - if (type is StorableType.File && !isFolder) - { - yield return new WindowsFile(pShellItem); - } - else if (type is StorableType.Folder && isFolder) - { - yield return new WindowsFolder(pShellItem); - } - else - { - continue; - } - } + List childItems = []; - yield break; + IShellItem* pChildShellItem = null; + while ((hr = pEnumShellItems.Get()->Next(1, &pChildShellItem)) == HRESULT.S_OK) + { + bool isFolder = pChildShellItem->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var dwAttributes).Succeeded && dwAttributes is SFGAO_FLAGS.SFGAO_FOLDER; - unsafe void GetEnumerator() + if (type.HasFlag(StorableType.File) && !isFolder) { - HRESULT hr = ThisPtr.Get()->BindToHandler(null, BHID.BHID_EnumItems, IID.IID_IEnumShellItems, (void**)pEnumShellItems.GetAddressOf()); - hr.ThrowIfFailedOnDebug(); + childItems.Add(new WindowsFile(pChildShellItem)); } - - unsafe bool GetNext() + else if (type.HasFlag(StorableType.Folder) && isFolder) { - HRESULT hr = pEnumShellItems.Get()->Next(1, pShellItem.GetAddressOf()); - return hr.ThrowIfFailedOnDebug() == HRESULT.S_OK; + childItems.Add(new WindowsFolder(pChildShellItem)); } } + + if (hr.ThrowIfFailedOnDebug().Failed) + return Enumerable.Empty().ToAsyncEnumerable(); + + return childItems.ToAsyncEnumerable(); + } + + public override void Dispose() + { + base.Dispose(); + + if (ShellNewMenu is not null) ShellNewMenu->Release(); } } } diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorable.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorable.cs index 3fdc51e33389..c01104a08d5b 100644 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorable.cs +++ b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorable.cs @@ -1,6 +1,7 @@ // Copyright (c) Files Community // Licensed under the MIT License. +using System.Runtime.CompilerServices; using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.System.SystemServices; @@ -8,59 +9,59 @@ namespace Files.App.Storage { - public abstract class WindowsStorable : IWindowsStorable, IStorableChild, IEquatable + public unsafe abstract class WindowsStorable : IWindowsStorable { - public ComPtr ThisPtr { get; protected set; } + public IShellItem* ThisPtr + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected internal set; + } + + public IContextMenu* ContextMenu + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected internal set; + } public string Id => this.GetDisplayName(SIGDN.SIGDN_FILESYSPATH); public string Name => this.GetDisplayName(SIGDN.SIGDN_PARENTRELATIVEFORUI); - public static unsafe WindowsStorable? TryParse(string parsablePath) + public static WindowsStorable? TryParse(string szPath) { HRESULT hr = default; - ComPtr pShellItem = default; - var IID_IShellItem = typeof(IShellItem).GUID; - - fixed (char* pszParsablePath = parsablePath) - { - hr = PInvoke.SHCreateItemFromParsingName( - pszParsablePath, - null, - &IID_IShellItem, - (void**)pShellItem.GetAddressOf()); - } - - if (pShellItem.IsNull) + IShellItem* pShellItem = null; + + fixed (char* pszPath = szPath) + hr = PInvoke.SHCreateItemFromParsingName(pszPath, null, IID.IID_IShellItem, (void**)&pShellItem); + + if (pShellItem is null) return null; - return pShellItem.HasShellAttributes(SFGAO_FLAGS.SFGAO_FOLDER) - ? new WindowsFolder(pShellItem) - : new WindowsFile(pShellItem); + return TryParse(pShellItem); } - public static unsafe WindowsStorable? TryParse(IShellItem* ptr) + public static WindowsStorable? TryParse(IShellItem* pShellItem) { - ComPtr pShellItem = default; - pShellItem.Attach(ptr); + bool isFolder = pShellItem->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var returnedAttributes).Succeeded && returnedAttributes == SFGAO_FLAGS.SFGAO_FOLDER; - return pShellItem.HasShellAttributes(SFGAO_FLAGS.SFGAO_FOLDER) - ? new WindowsFolder(pShellItem) - : new WindowsFile(pShellItem); + return isFolder ? new WindowsFolder(pShellItem) : new WindowsFile(pShellItem); } public unsafe Task GetParentAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); - ComPtr pParentFolder = default; - HRESULT hr = ThisPtr.Get()->GetParent(pParentFolder.GetAddressOf()); - if (hr.Failed) - { - if (!pParentFolder.IsNull) pParentFolder.Dispose(); - + IShellItem* pParentFolder = default; + HRESULT hr = ThisPtr->GetParent(&pParentFolder); + if (hr.ThrowIfFailedOnDebug().Failed) return Task.FromResult(null); - } return Task.FromResult(new WindowsFolder(pParentFolder)); } @@ -77,9 +78,10 @@ public override int GetHashCode() } /// - public void Dispose() + public virtual void Dispose() { - ThisPtr.Dispose(); + if (ThisPtr is not null) ThisPtr->Release(); + if (ContextMenu is not null) ContextMenu->Release(); } /// @@ -94,7 +96,7 @@ public unsafe bool Equals(IWindowsStorable? other) if (other is null) return false; - return ThisPtr.Get()->Compare(other.ThisPtr.Get(), (uint)_SICHINTF.SICHINT_DISPLAY, out int order).Succeeded && order is 0; + return ThisPtr->Compare(other.ThisPtr, (uint)_SICHINTF.SICHINT_DISPLAY, out int order).Succeeded && order is 0; } public static bool operator ==(WindowsStorable left, WindowsStorable right) diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Icon.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Icon.cs index 9b1fd95a31a5..f50de4bbad35 100644 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Icon.cs +++ b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Icon.cs @@ -45,7 +45,7 @@ public unsafe static HRESULT TryGetThumbnail(this IWindowsStorable storable, int thumbnailData = null; using ComPtr pShellItemImageFactory = default; - storable.ThisPtr.As(pShellItemImageFactory.GetAddressOf()); + storable.ThisPtr->QueryInterface(IID.IID_IShellItemImageFactory, (void**)pShellItemImageFactory.GetAddressOf()); if (pShellItemImageFactory.IsNull) return HRESULT.E_NOINTERFACE; @@ -267,10 +267,8 @@ public unsafe static HRESULT TrySetShortcutIcon(this IWindowsStorable storable, return HRESULT.E_INVALIDARG; using ComPtr pShellLink = default; - Guid IID_IShellLink = IShellLinkW.IID_Guid; - Guid BHID_SFUIObject = PInvoke.BHID_SFUIObject; - HRESULT hr = storable.ThisPtr.Get()->BindToHandler(null, &BHID_SFUIObject, &IID_IShellLink, (void**)pShellLink.GetAddressOf()); + HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IShellLinkW, (void**)pShellLink.GetAddressOf()); if (hr.ThrowIfFailedOnDebug().Failed) return hr; diff --git a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Shell.cs b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Shell.cs index c09b2fc0d103..a6bb70e4ced1 100644 --- a/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Shell.cs +++ b/src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Shell.cs @@ -17,7 +17,7 @@ public static partial class WindowsStorableHelpers public unsafe static HRESULT GetPropertyValue(this IWindowsStorable storable, string propKey, out TValue value) { using ComPtr pShellItem2 = default; - HRESULT hr = storable.ThisPtr.Get()->QueryInterface(IID.IID_IShellItem2, (void**)pShellItem2.GetAddressOf()); + HRESULT hr = storable.ThisPtr->QueryInterface(IID.IID_IShellItem2, (void**)pShellItem2.GetAddressOf()); PROPERTYKEY propertyKey = default; fixed (char* pszPropertyKey = propKey) @@ -33,10 +33,9 @@ public unsafe static HRESULT GetPropertyValue(this IWindowsStorable stor } if (typeof(TValue) == typeof(bool)) { - bool propertyValue = false; - hr = pShellItem2.Get()->GetBool(propertyKey, out var fPropertyValue); - propertyValue = fPropertyValue; - value = Unsafe.As(ref propertyValue); + bool fPropertyValue = false; + hr = pShellItem2.Get()->GetBool(&propertyKey, (BOOL*)&fPropertyValue); + value = Unsafe.As(ref fPropertyValue); return hr; } @@ -49,20 +48,13 @@ public unsafe static HRESULT GetPropertyValue(this IWindowsStorable stor public unsafe static bool HasShellAttributes(this IWindowsStorable storable, SFGAO_FLAGS attributes) { - return storable.ThisPtr.Get()->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var returnedAttributes).Succeeded && - returnedAttributes == attributes; - } - - public unsafe static bool HasShellAttributes(this ComPtr pShellItem, SFGAO_FLAGS attributes) - { - return pShellItem.Get()->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var returnedAttributes).Succeeded && - returnedAttributes == attributes; + return storable.ThisPtr->GetAttributes(attributes, out var dwRetAttributes).Succeeded && dwRetAttributes == attributes; } public unsafe static string GetDisplayName(this IWindowsStorable storable, SIGDN options = SIGDN.SIGDN_FILESYSPATH) { using ComHeapPtr pszName = default; - HRESULT hr = storable.ThisPtr.Get()->GetDisplayName(options, (PWSTR*)pszName.GetAddressOf()); + HRESULT hr = storable.ThisPtr->GetDisplayName(options, (PWSTR*)pszName.GetAddressOf()); return hr.ThrowIfFailedOnDebug().Succeeded ? new string((char*)pszName.Get()) // this is safe as it gets memcpy'd internally @@ -74,7 +66,7 @@ public unsafe static HRESULT TryInvokeContextMenuVerb(this IWindowsStorable stor Debug.Assert(Thread.CurrentThread.GetApartmentState() is ApartmentState.STA); using ComPtr pContextMenu = default; - HRESULT hr = storable.ThisPtr.Get()->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf()); + HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf()); HMENU hMenu = PInvoke.CreatePopupMenu(); hr = pContextMenu.Get()->QueryContextMenu(hMenu, 0, 1, 0x7FFF, PInvoke.CMF_OPTIMIZEFORINVOKE); @@ -99,7 +91,7 @@ public unsafe static HRESULT TryInvokeContextMenuVerbs(this IWindowsStorable sto Debug.Assert(Thread.CurrentThread.GetApartmentState() is ApartmentState.STA); using ComPtr pContextMenu = default; - HRESULT hr = storable.ThisPtr.Get()->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf()); + HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf()); HMENU hMenu = PInvoke.CreatePopupMenu(); hr = pContextMenu.Get()->QueryContextMenu(hMenu, 0, 1, 0x7FFF, PInvoke.CMF_OPTIMIZEFORINVOKE); @@ -130,7 +122,7 @@ public unsafe static HRESULT TryGetShellTooltip(this IWindowsStorable storable, tooltip = null; using ComPtr pQueryInfo = default; - HRESULT hr = storable.ThisPtr.Get()->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IQueryInfo, (void**)pQueryInfo.GetAddressOf()); + HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IQueryInfo, (void**)pQueryInfo.GetAddressOf()); if (hr.ThrowIfFailedOnDebug().Failed) return hr; @@ -143,5 +135,15 @@ public unsafe static HRESULT TryGetShellTooltip(this IWindowsStorable storable, return HRESULT.S_OK; } + + public unsafe static HRESULT TryPinFolderToQuickAccess() + { + return HRESULT.S_OK; + } + + public unsafe static HRESULT TryUnpinFolderFromQuickAccess() + { + return HRESULT.S_OK; + } } } diff --git a/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs index 2739fb84162a..fc447eedbe95 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs @@ -198,7 +198,7 @@ public override async Task ExecutePinToSidebarCommand(WidgetCardItem? item) unsafe { - hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, IID.IID_IShellItem, (IUnknown*)folderCardItem.Item.ThisPtr.Get(), pAgileReference.GetAddressOf()); + hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, IID.IID_IShellItem, (IUnknown*)folderCardItem.Item.ThisPtr, pAgileReference.GetAddressOf()); } // Pin to Quick Access on Windows @@ -206,9 +206,9 @@ public override async Task ExecutePinToSidebarCommand(WidgetCardItem? item) { unsafe { - using ComPtr pShellItem = default; - hr = pAgileReference.Get()->Resolve(IID.IID_IShellItem, (void**)pShellItem.GetAddressOf()); - var windowsFile = new WindowsFile(pShellItem); + IShellItem* pShellItem = null; + hr = pAgileReference.Get()->Resolve(IID.IID_IShellItem, (void**)&pShellItem); + using var windowsFile = new WindowsFile(pShellItem); // NOTE: "pintohome" is an undocumented verb, which calls an undocumented COM class, windows.storage.dll!CPinToFrequentExecute : public IExecuteCommand, ... return windowsFile.TryInvokeContextMenuVerb("pintohome"); @@ -234,7 +234,7 @@ public override async Task ExecuteUnpinFromSidebarCommand(WidgetCardItem? item) unsafe { - hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, IID.IID_IShellItem, (IUnknown*)folderCardItem.Item.ThisPtr.Get(), pAgileReference.GetAddressOf()); + hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, IID.IID_IShellItem, (IUnknown*)folderCardItem.Item.ThisPtr, pAgileReference.GetAddressOf()); } // Unpin from Quick Access on Windows @@ -242,9 +242,9 @@ public override async Task ExecuteUnpinFromSidebarCommand(WidgetCardItem? item) { unsafe { - using ComPtr pShellItem = default; - hr = pAgileReference.Get()->Resolve(IID.IID_IShellItem, (void**)pShellItem.GetAddressOf()); - var windowsFile = new WindowsFile(pShellItem); + IShellItem* pShellItem = null; + hr = pAgileReference.Get()->Resolve(IID.IID_IShellItem, (void**)&pShellItem); + using var windowsFile = new WindowsFile(pShellItem); // NOTE: "unpinfromhome" is an undocumented verb, which calls an undocumented COM class, windows.storage.dll!CRemoveFromFrequentPlacesExecute : public IExecuteCommand, ... // NOTE: "remove" is for some shell folders where the "unpinfromhome" may not work