From 15f5dc45a65cee694042d579b220276e8d39d9df Mon Sep 17 00:00:00 2001 From: 0x5BFA <62196528+0x5bfa@users.noreply.github.com> Date: Sat, 4 Jul 2026 00:44:54 +0900 Subject: [PATCH] Init --- Directory.Packages.props | 2 +- src/Files.App.CsWin32/ComHelpers.cs | 24 ++ src/Files.App.CsWin32/ComPtr`1.cs | 96 ----- src/Files.App.CsWin32/Extras.cs | 24 +- .../Files.App.CsWin32.csproj | 1 + .../HStringStringMarshaller.cs | 122 +++++++ src/Files.App.CsWin32/IDetectionAndSharing.cs | 64 ++-- src/Files.App.CsWin32/IOpenControlPanel.cs | 20 +- .../IStorageProviderQuotaUI.cs | 44 ++- .../IStorageProviderStatusUI.cs | 75 +++- .../IStorageProviderStatusUISource.cs | 38 +- .../IStorageProviderStatusUISourceFactory.cs | 29 +- src/Files.App.CsWin32/ManualGuid.cs | 122 +------ src/Files.App.CsWin32/NativeMethods.json | 3 +- src/Files.App.CsWin32/NativeMethods.txt | 13 + src/Files.App.Server/Helpers.cs | 8 +- src/Files.App.Server/Program.cs | 6 +- .../Helpers/WindowsStorableHelpers.Icon.cs | 25 +- .../Helpers/WindowsStorableHelpers.Shell.cs | 175 +++++----- .../Windows/IWindowsFolder.cs | 6 +- .../Windows/IWindowsStorable.cs | 8 +- .../Windows/Managers/SystemTrayManager.cs | 1 + .../Windows/Managers/TaskbarManager.cs | 22 +- .../Windows/Managers/WindowsDriveManager.cs | 2 +- .../Managers/WindowsFolderChangeWatcher.cs | 1 + .../Windows/Managers/WindowsObjectPicker.cs | 50 ++- .../Windows/WindowsBulkOperations.cs | 38 +- .../WindowsBulkOperationsSink.Methods.cs | 59 ++-- .../WindowsBulkOperationsSink.VTable.cs | 161 --------- src/Files.App.Storage/Windows/WindowsFile.cs | 6 +- .../Windows/WindowsFolder.cs | 29 +- .../Windows/WindowsStorable.cs | 38 +- src/Files.App/Data/Items/WidgetRecentItem.cs | 6 +- src/Files.App/Data/Items/WindowEx.cs | 2 + .../Storage/StorageTrashBinService.cs | 66 ++-- .../Services/Windows/WindowsDialogService.cs | 102 +++--- .../Windows/WindowsRecentItemsService.cs | 61 ++-- .../Windows/WindowsWallpaperService.cs | 25 +- .../Utils/Shell/DetectionAndSharingHelper.cs | 24 +- src/Files.App/Utils/Shell/ItemStreamHelper.cs | 31 +- src/Files.App/Utils/Shell/LaunchHelper.cs | 8 +- src/Files.App/Utils/Shell/OpenWithMenu.cs | 66 +--- src/Files.App/Utils/Shell/PreviewHandler.cs | 330 ++++-------------- .../Utils/Storage/Helpers/MtpHelpers.cs | 32 +- .../Utils/Storage/Helpers/SyncRootHelpers.cs | 33 +- .../Utils/Taskbar/SystemTrayIconWindow.cs | 1 + .../Previews/ShellPreviewViewModel.cs | 50 +-- .../Widgets/QuickAccessWidgetViewModel.cs | 54 ++- .../Generators/VTableFunctionGenerator.cs | 115 ------ .../GeneratedVTableFunctionAttribute.cs | 12 - 50 files changed, 944 insertions(+), 1386 deletions(-) create mode 100644 src/Files.App.CsWin32/ComHelpers.cs delete mode 100644 src/Files.App.CsWin32/ComPtr`1.cs create mode 100644 src/Files.App.CsWin32/HStringStringMarshaller.cs delete mode 100644 src/Files.App.Storage/Windows/WindowsBulkOperationsSink.VTable.cs delete mode 100644 src/Files.Core.SourceGenerator/Generators/VTableFunctionGenerator.cs delete mode 100644 src/Files.Shared/Attributes/GeneratedVTableFunctionAttribute.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 4da5c20e20c9..ddda8e9d3b77 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -43,7 +43,7 @@ - + diff --git a/src/Files.App.CsWin32/ComHelpers.cs b/src/Files.App.CsWin32/ComHelpers.cs new file mode 100644 index 000000000000..6febcaa134ff --- /dev/null +++ b/src/Files.App.CsWin32/ComHelpers.cs @@ -0,0 +1,24 @@ +// Copyright (c) Files Community +// SPDX-License-Identifier: MPL-2.0 + +using System; +using Windows.Win32.Foundation; +using Windows.Win32.System.Com; +using Windows.Win32.UI.Shell; + +namespace Windows.Win32; + +public static class ComHelpers +{ + public static HRESULT TryCast(object nativeObject, out TInterface? instance) + where TInterface : class + { + instance = null; + + if (nativeObject is not TInterface casted) + return HRESULT.E_NOINTERFACE; + + instance = casted; + return HRESULT.S_OK; + } +} diff --git a/src/Files.App.CsWin32/ComPtr`1.cs b/src/Files.App.CsWin32/ComPtr`1.cs deleted file mode 100644 index 2cceed532893..000000000000 --- a/src/Files.App.CsWin32/ComPtr`1.cs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) Files Community -// Licensed under the MIT License. - -using System; -using System.Runtime.CompilerServices; -using Windows.Win32.Foundation; -using Windows.Win32.System.Com; - -namespace Windows.Win32 -{ - /// - /// Contains a COM pointer and a set of methods to work with the pointer safely. - /// - public unsafe struct ComPtr : IDisposable where T : unmanaged, IComIID - { - private T* _ptr; - - public readonly bool IsNull - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _ptr is null; - } - - // Constructors - - public ComPtr(T* ptr) - { - _ptr = ptr; - - if (ptr is not null) - ((IUnknown*)ptr)->AddRef(); - } - - // Methods - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Attach(T* other) - { - if (_ptr is not null) - ((IUnknown*)_ptr)->Release(); - - _ptr = other; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T* Detach() - { - T* ptr = _ptr; - _ptr = null; - return ptr; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly T* Get() - { - return _ptr; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly T** GetAddressOf() - { - return (T**)Unsafe.AsPointer(ref Unsafe.AsRef(in this)); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly HRESULT As(U** other) where U : unmanaged, IComIID - { - return ((IUnknown*)_ptr)->QueryInterface((Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in U.Guid)), (void**)other); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly HRESULT As(Guid* riid, IUnknown** other) where U : unmanaged, IComIID - { - return ((IUnknown*)_ptr)->QueryInterface(riid, (void**)other); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly HRESULT CoCreateInstance(Guid* rclsid, IUnknown* pUnkOuter = null, CLSCTX dwClsContext = CLSCTX.CLSCTX_LOCAL_SERVER) - { - return PInvoke.CoCreateInstance(rclsid, pUnkOuter, dwClsContext, (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in T.Guid)), (void**)this.GetAddressOf()); - } - - // Disposer - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Dispose() - { - T* ptr = _ptr; - if (ptr is not null) - { - _ptr = null; - ((IUnknown*)ptr)->Release(); - } - } - } -} diff --git a/src/Files.App.CsWin32/Extras.cs b/src/Files.App.CsWin32/Extras.cs index 67b62cf0e28d..10b91a4afc49 100644 --- a/src/Files.App.CsWin32/Extras.cs +++ b/src/Files.App.CsWin32/Extras.cs @@ -1,25 +1,15 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using Windows.Win32.Foundation; +using Windows.Win32.Graphics.DirectComposition; +using Windows.Win32.Graphics.Gdi; using Windows.Win32.UI.WindowsAndMessaging; namespace Windows.Win32 { - namespace Graphics.Gdi - { - [UnmanagedFunctionPointer(CallingConvention.Winapi)] - public unsafe delegate BOOL MONITORENUMPROC([In] HMONITOR param0, [In] HDC param1, [In][Out] RECT* param2, [In] LPARAM param3); - } - - namespace UI.WindowsAndMessaging - { - [UnmanagedFunctionPointer(CallingConvention.Winapi)] - public delegate LRESULT WNDPROC(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam); - } - public static partial class PInvoke { [LibraryImport("User32", EntryPoint = "SetWindowLongW")] @@ -46,11 +36,17 @@ public static unsafe nint SetWindowLongPtr(HWND hWnd, WINDOW_LONG_PTR_INDEX nInd namespace Extras { + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public unsafe delegate BOOL ManagedMONITORENUMPROC([In] HMONITOR param0, [In] HDC param1, [In][Out] RECT* param2, [In] LPARAM param3); + + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + public delegate LRESULT ManagedWNDPROC(HWND hWnd, uint msg, WPARAM wParam, LPARAM lParam); + [GeneratedComInterface, Guid("EACDD04C-117E-4E17-88F4-D1B12B0E3D89"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public partial interface IDCompositionTarget { [PreserveSig] - int SetRoot(nint visual); + int SetRoot(IDCompositionVisual visual); } } } diff --git a/src/Files.App.CsWin32/Files.App.CsWin32.csproj b/src/Files.App.CsWin32/Files.App.CsWin32.csproj index 2b9f3ea2b2f3..8772ede69e39 100644 --- a/src/Files.App.CsWin32/Files.App.CsWin32.csproj +++ b/src/Files.App.CsWin32/Files.App.CsWin32.csproj @@ -11,6 +11,7 @@ win-x86;win-x64;win-arm64 true true + true true diff --git a/src/Files.App.CsWin32/HStringStringMarshaller.cs b/src/Files.App.CsWin32/HStringStringMarshaller.cs new file mode 100644 index 000000000000..84ece6831d61 --- /dev/null +++ b/src/Files.App.CsWin32/HStringStringMarshaller.cs @@ -0,0 +1,122 @@ +// Copyright (c) Files Community +// SPDX-License-Identifier: MPL-2.0 + +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using Windows.Win32.Foundation; +using Windows.Win32.System.WinRT; + +namespace Windows.Win32; + +[CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedIn, typeof(HStringStringMarshaller.ManagedToUnmanagedIn))] +[CustomMarshaller(typeof(string), MarshalMode.ManagedToUnmanagedOut, typeof(HStringStringMarshaller.ManagedToUnmanagedOut))] +[CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedIn, typeof(HStringStringMarshaller.UnmanagedToManagedIn))] +[CustomMarshaller(typeof(string), MarshalMode.UnmanagedToManagedOut, typeof(HStringStringMarshaller.UnmanagedToManagedOut))] +internal static unsafe class HStringStringMarshaller +{ + public ref struct ManagedToUnmanagedIn + { + private nint _hstring; + + public void FromManaged(string? managed) + { + _hstring = CreateHString(managed); + } + + public nint ToUnmanaged() + => _hstring; + + public void Free() + { + DeleteHString(_hstring); + } + } + + public ref struct UnmanagedToManagedOut + { + private nint _hstring; + + public void FromManaged(string? managed) + { + _hstring = CreateHString(managed); + } + + public nint ToUnmanaged() + => _hstring; + + public void Free() + { + } + } + + public ref struct UnmanagedToManagedIn + { + private nint _hstring; + + public void FromUnmanaged(nint unmanaged) + { + _hstring = unmanaged; + } + + public string? ToManaged() + { + return ToManagedString(_hstring); + } + + public void Free() + { + } + } + + public ref struct ManagedToUnmanagedOut + { + private nint _hstring; + + public void FromUnmanaged(nint unmanaged) + { + _hstring = unmanaged; + } + + public string? ToManaged() + { + return ToManagedString(_hstring); + } + + public void Free() + { + DeleteHString(_hstring); + } + } + + private static nint CreateHString(string? managed) + { + if (managed is null) + return 0; + + HSTRING hstring; + fixed (char* sourceString = managed) + { + HRESULT hr = PInvoke.WindowsCreateString(new(sourceString), checked((uint)managed.Length), &hstring); + if (hr.Failed) + Marshal.ThrowExceptionForHR(hr.Value); + } + + return hstring; + } + + private static string? ToManagedString(nint hstring) + { + if (hstring == 0) + return null; + + uint length; + PCWSTR buffer = PInvoke.WindowsGetStringRawBuffer(new HSTRING(hstring), &length); + return new string((char*)buffer.Value, 0, checked((int)length)); + } + + private static void DeleteHString(nint hstring) + { + if (hstring != 0) + PInvoke.WindowsDeleteString(new HSTRING(hstring)); + } +} diff --git a/src/Files.App.CsWin32/IDetectionAndSharing.cs b/src/Files.App.CsWin32/IDetectionAndSharing.cs index b06562b4815a..ca0f3c0dc4ca 100644 --- a/src/Files.App.CsWin32/IDetectionAndSharing.cs +++ b/src/Files.App.CsWin32/IDetectionAndSharing.cs @@ -1,35 +1,45 @@ // Copyright (c) Files Community // SPDX-License-Identifier: MPL-2.0 -using Files.Shared.Attributes; using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using Windows.Win32.Foundation; -namespace Windows.Win32.UI.Shell +namespace Windows.Win32.UI.Shell; + +[GeneratedComInterface, Guid("1FDA955C-61FF-11DA-978C-0008744FAAB7"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +public partial interface IDetectionAndSharing +{ + [PreserveSig] + HRESULT GetStatus(DTSH_TYPE type, out DTSH_STATE state, out DTSH_ACTION action); + + [PreserveSig] + int TurnOn(nint hwnd, DTSH_TYPE type, int value); + + [PreserveSig] + int GetCurrentFwProfile(out /*NetFwProfileType2*/ int profile); + + [PreserveSig] + int GetStatusForProfile(/*NetFwProfileType2*/ int profile, DTSH_TYPE type, out DTSH_STATE state, out DTSH_ACTION action); + + [PreserveSig] + int TurnOnForProfile(nint hwnd, /*NetFwProfileType2*/ int profile, DTSH_TYPE type, int value); +} + +public enum DTSH_TYPE +{ + DTSH_NETWORK_DISCOVERY = 0, + DTSH_FILE_SHARING = 1, +} + +public enum DTSH_STATE +{ + DTSH_OFF = 0, + DTSH_ON = 1, +} + +public enum DTSH_ACTION { - public unsafe partial struct IDetectionAndSharing : IComIID - { - [GeneratedVTableFunction(Index = 3)] - public partial HRESULT GetStatus(DTSH_TYPE type, DTSH_STATE* state, DTSH_ACTION* action); - - [GuidRVAGen.Guid("1FDA955C-61FF-11DA-978C-0008744FAAB7")] - public static partial ref readonly Guid Guid { get; } - } - - public enum DTSH_TYPE - { - DTSH_NETWORK_DISCOVERY = 0, - DTSH_FILE_SHARING = 1, - } - - public enum DTSH_STATE - { - DTSH_OFF = 0, - DTSH_ON = 1, - } - - public enum DTSH_ACTION - { - DTSH_NONE = 0, - } + DTSH_NONE = 0, } diff --git a/src/Files.App.CsWin32/IOpenControlPanel.cs b/src/Files.App.CsWin32/IOpenControlPanel.cs index 45036b1e4a92..72e9e33e7374 100644 --- a/src/Files.App.CsWin32/IOpenControlPanel.cs +++ b/src/Files.App.CsWin32/IOpenControlPanel.cs @@ -1,19 +1,19 @@ // Copyright (c) Files Community // SPDX-License-Identifier: MPL-2.0 -using Files.Shared.Attributes; using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using Windows.Win32.Foundation; -using Windows.Win32.System.Com; -namespace Windows.Win32.UI.Shell +namespace Windows.Win32.UI.Shell; + +[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16), Guid("D11AD862-66DE-4DF4-BF6C-1F5621996AF1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +public unsafe partial interface IOpenControlPanel { - public unsafe partial struct IOpenControlPanel : IComIID - { - [GeneratedVTableFunction(Index = 3)] - public partial HRESULT Open(char* name, char* page, void* site); + [PreserveSig] + HRESULT Open(PCWSTR name, PCWSTR page, void* site); - [GuidRVAGen.Guid("D11AD862-66DE-4DF4-BF6C-1F5621996AF1")] - public static partial ref readonly Guid Guid { get; } - } + [PreserveSig] + HRESULT GetPath(string name, nint path, uint pathLength); } diff --git a/src/Files.App.CsWin32/IStorageProviderQuotaUI.cs b/src/Files.App.CsWin32/IStorageProviderQuotaUI.cs index c7cd816b77fc..b620d5123220 100644 --- a/src/Files.App.CsWin32/IStorageProviderQuotaUI.cs +++ b/src/Files.App.CsWin32/IStorageProviderQuotaUI.cs @@ -1,21 +1,47 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. -using Files.Shared.Attributes; using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using Windows.Win32.Foundation; namespace Windows.Win32.System.WinRT { - public unsafe partial struct IStorageProviderQuotaUI : IComIID + [GeneratedComInterface, Guid("BA6295C3-312E-544F-9FD5-1F81B21F3649"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public partial interface IStorageProviderQuotaUI { - [GeneratedVTableFunction(Index = 6)] - public partial HRESULT GetQuotaTotalInBytes(ulong* value); + [PreserveSig] + HRESULT GetIids(out uint iidCount, out nint iids); - [GeneratedVTableFunction(Index = 8)] - public partial HRESULT GetQuotaUsedInBytes(ulong* value); + [PreserveSig] + HRESULT GetRuntimeClassName([MarshalUsing(typeof(global::Windows.Win32.HStringStringMarshaller))] out string? className); - [GuidRVAGen.Guid("BA6295C3-312E-544F-9FD5-1F81B21F3649")] - public static partial ref readonly Guid Guid { get; } + [PreserveSig] + HRESULT GetTrustLevel(out TrustLevel trustLevel); + + [PreserveSig] + HRESULT GetQuotaTotalInBytes(out ulong value); + + [PreserveSig] + HRESULT PutQuotaTotalInBytes(ulong value); + + [PreserveSig] + HRESULT GetQuotaUsedInBytes(out ulong value); + + [PreserveSig] + HRESULT PutQuotaUsedInBytes(ulong value); + + [PreserveSig] + HRESULT GetQuotaUsedLabel([MarshalUsing(typeof(global::Windows.Win32.HStringStringMarshaller))] out string? value); + + [PreserveSig] + HRESULT PutQuotaUsedLabel([MarshalUsing(typeof(global::Windows.Win32.HStringStringMarshaller))] string? value); + + [PreserveSig] + HRESULT GetQuotaUsedColor(out nint value); + + [PreserveSig] + HRESULT PutQuotaUsedColor(nint value); } } diff --git a/src/Files.App.CsWin32/IStorageProviderStatusUI.cs b/src/Files.App.CsWin32/IStorageProviderStatusUI.cs index 3aa60c4255f9..6e4bc121c63f 100644 --- a/src/Files.App.CsWin32/IStorageProviderStatusUI.cs +++ b/src/Files.App.CsWin32/IStorageProviderStatusUI.cs @@ -1,18 +1,71 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. -using Files.Shared.Attributes; using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using Windows.Storage.Provider; using Windows.Win32.Foundation; -namespace Windows.Win32.System.WinRT +namespace Windows.Win32.System.WinRT; + +[GeneratedComInterface, Guid("D6B6A758-198D-5B80-977F-5FF73DA33118"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +public partial interface IStorageProviderStatusUI { - public unsafe partial struct IStorageProviderStatusUI : IComIID - { - [GeneratedVTableFunction(Index = 14)] - public partial HRESULT GetQuotaUI(IStorageProviderQuotaUI** result); - - [GuidRVAGen.Guid("D6B6A758-198D-5B80-977F-5FF73DA33118")] - public static partial ref readonly Guid Guid { get; } - } + [PreserveSig] + HRESULT GetIids(out uint iidCount, out /* IID** */ nint iids); + + [PreserveSig] + HRESULT GetRuntimeClassName([MarshalUsing(typeof(global::Windows.Win32.HStringStringMarshaller))] out string? className); + + [PreserveSig] + HRESULT GetTrustLevel(out TrustLevel trustLevel); + + [PreserveSig] + HRESULT GetProviderState(out StorageProviderState value); + + [PreserveSig] + HRESULT PutProviderState(StorageProviderState value); + + [PreserveSig] + HRESULT GetProviderStateLabel([MarshalUsing(typeof(global::Windows.Win32.HStringStringMarshaller))] out string? value); + + [PreserveSig] + HRESULT PutProviderStateLabel([MarshalUsing(typeof(global::Windows.Win32.HStringStringMarshaller))] string? value); + + [PreserveSig] + HRESULT GetProviderStateIcon(out /* Windows.Foundation.Uri** */ nint value); + + [PreserveSig] + HRESULT PutProviderStateIcon(/* Windows.Foundation.Uri* */ nint value); + + [PreserveSig] + HRESULT GetSyncStatusCommand(out /* IStorageProviderUICommand** */ nint value); + + [PreserveSig] + HRESULT PutSyncStatusCommand(/* IStorageProviderUICommand* */ nint value); + + [PreserveSig] + HRESULT GetQuotaUI([MarshalAs(UnmanagedType.Interface)] out IStorageProviderQuotaUI value); + + [PreserveSig] + HRESULT PutQuotaUI([MarshalAs(UnmanagedType.Interface)] IStorageProviderQuotaUI value); + + [PreserveSig] + HRESULT GetMoreInfoUI(out /* IStorageProviderMoreInfoUI** */ nint value); + + [PreserveSig] + HRESULT PutMoreInfoUI(/* IStorageProviderMoreInfoUI* */ nint value); + + [PreserveSig] + HRESULT GetProviderPrimaryCommand(out /* IStorageProviderUICommand** */ nint value); + + [PreserveSig] + HRESULT PutProviderPrimaryCommand(/* IStorageProviderUICommand* */ nint value); + + [PreserveSig] + HRESULT GetProviderSecondaryCommands(out /* IVector** */ nint value); + + [PreserveSig] + HRESULT PutProviderSecondaryCommands(/* IVector* */ nint value); } diff --git a/src/Files.App.CsWin32/IStorageProviderStatusUISource.cs b/src/Files.App.CsWin32/IStorageProviderStatusUISource.cs index 9f4ede28194f..6af1b6677679 100644 --- a/src/Files.App.CsWin32/IStorageProviderStatusUISource.cs +++ b/src/Files.App.CsWin32/IStorageProviderStatusUISource.cs @@ -1,18 +1,34 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. -using Files.Shared.Attributes; using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using Windows.Win32.Foundation; +using WinRT; -namespace Windows.Win32.System.WinRT +namespace Windows.Win32.System.WinRT; + +[GeneratedComInterface, Guid("A306C249-3D66-5E70-9007-E43DF96051FF"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +public partial interface IStorageProviderStatusUISource { - public unsafe partial struct IStorageProviderStatusUISource : IComIID - { - [GeneratedVTableFunction(Index = 6)] - public partial HRESULT GetStatusUI(IStorageProviderStatusUI** result); - - [GuidRVAGen.Guid("A306C249-3D66-5E70-9007-E43DF96051FF")] - public static partial ref readonly Guid Guid { get; } - } + [PreserveSig] + HRESULT GetIids(out uint iidCount, out /* IID** */ nint iids); + + [PreserveSig] + HRESULT GetRuntimeClassName([MarshalUsing(typeof(global::Windows.Win32.HStringStringMarshaller))] out string? className); + + [PreserveSig] + HRESULT GetTrustLevel(out TrustLevel trustLevel); + + [PreserveSig] + HRESULT GetStatusUI([MarshalAs(UnmanagedType.Interface)] out IStorageProviderStatusUI result); + + [PreserveSig] + HRESULT AddStatusUIChanged( + /* TypedEventHandler* */ nint handler, + out EventRegistrationToken token); + + [PreserveSig] + HRESULT RemoveStatusUIChanged(EventRegistrationToken token); } diff --git a/src/Files.App.CsWin32/IStorageProviderStatusUISourceFactory.cs b/src/Files.App.CsWin32/IStorageProviderStatusUISourceFactory.cs index 4065c1800c10..6d7a1e1a5513 100644 --- a/src/Files.App.CsWin32/IStorageProviderStatusUISourceFactory.cs +++ b/src/Files.App.CsWin32/IStorageProviderStatusUISourceFactory.cs @@ -1,18 +1,27 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. -using Files.Shared.Attributes; using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using Windows.Win32.Foundation; -namespace Windows.Win32.System.WinRT +namespace Windows.Win32.System.WinRT; + +[GeneratedComInterface, Guid("12E46B74-4E5A-58D1-A62F-0376E8EE7DD8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +public partial interface IStorageProviderStatusUISourceFactory { - public unsafe partial struct IStorageProviderStatusUISourceFactory : IComIID - { - [GeneratedVTableFunction(Index = 6)] - public partial HRESULT GetStatusUISource(nint syncRootId, IStorageProviderStatusUISource** result); + [PreserveSig] + HRESULT GetIids(out uint iidCount, out /* IID** */ nint iids); + + [PreserveSig] + HRESULT GetRuntimeClassName([MarshalUsing(typeof(global::Windows.Win32.HStringStringMarshaller))] out string? className); + + [PreserveSig] + HRESULT GetTrustLevel(out TrustLevel trustLevel); - [GuidRVAGen.Guid("12E46B74-4E5A-58D1-A62F-0376E8EE7DD8")] - public static partial ref readonly Guid Guid { get; } - } + [PreserveSig] + HRESULT GetStatusUISource( + [MarshalUsing(typeof(global::Windows.Win32.HStringStringMarshaller))] string? syncRootId, + [MarshalAs(UnmanagedType.Interface)] out IStorageProviderStatusUISource result); } diff --git a/src/Files.App.CsWin32/ManualGuid.cs b/src/Files.App.CsWin32/ManualGuid.cs index 721d5e0ca2d7..0927ba553eee 100644 --- a/src/Files.App.CsWin32/ManualGuid.cs +++ b/src/Files.App.CsWin32/ManualGuid.cs @@ -2,125 +2,23 @@ // SPDX-License-Identifier: MPL-2.0 using System; -using System.Runtime.CompilerServices; using Windows.Win32.System.WinRT; namespace Windows.Win32 { - public static unsafe partial class IID + public static partial class CLSID { - public static Guid* IID_IStorageProviderStatusUISourceFactory - => (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IStorageProviderStatusUISourceFactory.Guid)); - - [GuidRVAGen.Guid("000214E4-0000-0000-C000-000000000046")] - public static partial Guid* IID_IContextMenu { get; } - - [GuidRVAGen.Guid("70629033-E363-4A28-A567-0DB78006E6D7")] - public static partial Guid* IID_IEnumShellItems { get; } - - [GuidRVAGen.Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE")] - public static partial Guid* IID_IShellItem { get; } - - [GuidRVAGen.Guid("7E9FB0D3-919F-4307-AB2E-9B1860310C93")] - public static partial Guid* IID_IShellItem2 { get; } - - [GuidRVAGen.Guid("947AAB5F-0A5C-4C13-B4D6-4BF7836FC9F8")] - public static partial Guid* IID_IFileOperation { get; } - - [GuidRVAGen.Guid("D57C7288-D4AD-4768-BE02-9D969532D960")] - public static partial Guid* IID_IFileOpenDialog { get; } - - [GuidRVAGen.Guid("84BCCD23-5FDE-4CDB-AEA4-AF64B83D78AB")] - public static partial Guid* IID_IFileSaveDialog { get; } - - [GuidRVAGen.Guid("B92B56A9-8B55-4E14-9A89-0199BBB6F93B")] - public static partial Guid* IID_IDesktopWallpaper { get; } - - [GuidRVAGen.Guid("2E941141-7F97-4756-BA1D-9DECDE894A3D")] - public static partial Guid* IID_IApplicationActivationManager { get; } - - [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; } - - [GuidRVAGen.Guid("B63EA76D-1F85-456F-A19C-48159EFA858B")] - public static partial Guid* IID_IShellItemArray { get; } - - [GuidRVAGen.Guid("7F9185B0-CB92-43C5-80A9-92277A4F7B54")] - public static partial Guid* IID_IExecuteCommand { get; } - - [GuidRVAGen.Guid("1C9CD5BB-98E9-4491-A60F-31AACC72B83C")] - public static partial Guid* IID_IObjectWithSelection { get; } - - [GuidRVAGen.Guid("000214E8-0000-0000-C000-000000000046")] - public static partial Guid* IID_IShellExtInit { get; } - - [GuidRVAGen.Guid("000214F4-0000-0000-C000-000000000046")] - public static partial Guid* IID_IContextMenu2 { get; } - - [GuidRVAGen.Guid("0000010E-0000-0000-C000-000000000046")] - public static partial Guid* IID_IDataObject { get; } + public static Guid CLSID_PinToFrequentExecute { get; } = new(0xB455F46Eu, 0xE4AF, 0x4035, 0xB0, 0xA4, 0xCF, 0x18, 0xD2, 0xF6, 0xF2, 0x8E); + public static Guid CLSID_UnPinFromFrequentExecute { get; } = new(0xEE20EEBAu, 0xDF64, 0x4A4E, 0xB7, 0xBB, 0x2D, 0x1C, 0x6B, 0x2D, 0xFC, 0xC1); + public static Guid CLSID_NewMenu { get; } = new(0xD969A300u, 0xE7FF, 0x11D0, 0xA9, 0x3B, 0x00, 0xA0, 0xC9, 0x0F, 0x27, 0x19); + public static Guid CLSID_OpenWithMenu { get; } = new(0x09799AFBu, 0xAD67, 0x11D1, 0xAB, 0xCD, 0x00, 0xC0, 0x4F, 0xC3, 0x09, 0x36); + public static Guid CLSID_DetectionAndSharing { get; } = new(0x1FDA955Bu, 0x61FF, 0x11DA, 0x97, 0x8C, 0x00, 0x08, 0x74, 0x4F, 0xAA, 0xB7); + public static Guid CLSID_OpenControlPanel { get; } = new(0x06622D85u, 0x6856, 0x4460, 0x8D, 0xE1, 0xA8, 0x19, 0x21, 0xB4, 0x1C, 0x4B); } - public static unsafe partial class CLSID + public static partial class FOLDERID { - [GuidRVAGen.Guid("3AD05575-8857-4850-9277-11B85BDB8E09")] - public static partial Guid* CLSID_FileOperation { get; } - - [GuidRVAGen.Guid("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7")] - public static partial Guid* CLSID_FileOpenDialog { get; } - - [GuidRVAGen.Guid("C0B4E2F3-BA21-4773-8DBA-335EC946EB8B")] - public static partial Guid* CLSID_FileSaveDialog { get; } - - [GuidRVAGen.Guid("C2CF3110-460E-4FC1-B9D0-8A1C0C9CC4BD")] - public static partial Guid* CLSID_DesktopWallpaper { get; } - - [GuidRVAGen.Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")] - public static partial Guid* CLSID_ApplicationActivationManager { get; } - - [GuidRVAGen.Guid("B455F46E-E4AF-4035-B0A4-CF18D2F6F28E")] - public static partial Guid* CLSID_PinToFrequentExecute { get; } - - [GuidRVAGen.Guid("EE20EEBA-DF64-4A4E-B7BB-2D1C6B2DFCC1")] - public static partial Guid* CLSID_UnPinFromFrequentExecute { get; } - - [GuidRVAGen.Guid("D969A300-E7FF-11d0-A93B-00A0C90F2719")] - public static partial Guid* CLSID_NewMenu { get; } - - [GuidRVAGen.Guid("09799AFB-AD67-11D1-ABCD-00C04FC30936")] - public static partial Guid* CLSID_OpenWithMenu { get; } - - [GuidRVAGen.Guid("1FDA955B-61FF-11DA-978C-0008744FAAB7")] - public static partial Guid* CLSID_DetectionAndSharing { get; } - - [GuidRVAGen.Guid("06622D85-6856-4460-8DE1-A81921B41C4B")] - public static partial Guid* CLSID_OpenControlPanel { get; } - } - - public static unsafe partial class BHID - { - [GuidRVAGen.Guid("3981E225-F559-11D3-8E3A-00C04F6837D5")] - public static partial Guid* BHID_SFUIObject { get; } - - [GuidRVAGen.Guid("94F60519-2850-4924-AA5A-D15E84868039")] - public static partial Guid* BHID_EnumItems { get; } - - [GuidRVAGen.Guid("B8C0BD9F-ED24-455C-83E6-D5390C4FE8C4")] - public static partial Guid* BHID_DataObject { get; } - } - - public static unsafe partial class FOLDERID - { - [GuidRVAGen.Guid("B7534046-3ECB-4C18-BE4E-64CD4CB7D6AC")] - public static partial Guid* FOLDERID_RecycleBinFolder { get; } - - [GuidRVAGen.Guid("0AC0837C-BBF8-452A-850D-79D08E667CA7")] - public static partial Guid* FOLDERID_ComputerFolder { get; } + public static Guid FOLDERID_RecycleBinFolder { get; } = new(0xB7534046u, 0x3ECB, 0x4C18, 0xBE, 0x4E, 0x64, 0xCD, 0x4C, 0xB7, 0xD6, 0xAC); + public static Guid FOLDERID_ComputerFolder { get; } = new(0x0AC0837Cu, 0xBBF8, 0x452A, 0x85, 0x0D, 0x79, 0xD0, 0x8E, 0x66, 0x7C, 0xA7); } } diff --git a/src/Files.App.CsWin32/NativeMethods.json b/src/Files.App.CsWin32/NativeMethods.json index 2e32fa443533..ac009d621b98 100644 --- a/src/Files.App.CsWin32/NativeMethods.json +++ b/src/Files.App.CsWin32/NativeMethods.json @@ -1,8 +1,9 @@ { "$schema": "https://aka.ms/CsWin32.schema.json", - "allowMarshaling": false, + "allowMarshaling": true, "public": true, "comInterop": { + "useComSourceGenerators": true, "preserveSigMethods": [ "*" ] diff --git a/src/Files.App.CsWin32/NativeMethods.txt b/src/Files.App.CsWin32/NativeMethods.txt index d67b29b50ee0..c8fef8c36c9f 100644 --- a/src/Files.App.CsWin32/NativeMethods.txt +++ b/src/Files.App.CsWin32/NativeMethods.txt @@ -324,3 +324,16 @@ SafeArrayUnaccessData SafeArrayGetDim SafeArrayGetLBound SafeArrayGetUBound +IObjectWithSite +IPreviewHandlerVisuals +IPreviewHandlerFrame +IInitializeWithStream +IInitializeWithFile +IInitializeWithItem +WindowsGetStringRawBuffer +BHID_* +FileOperation +FileOpenDialog +FileSaveDialog +DesktopWallpaper +ApplicationActivationManager diff --git a/src/Files.App.Server/Helpers.cs b/src/Files.App.Server/Helpers.cs index 8ab3253540d5..86f58f9e04bb 100644 --- a/src/Files.App.Server/Helpers.cs +++ b/src/Files.App.Server/Helpers.cs @@ -1,4 +1,4 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. using System.Runtime.CompilerServices; @@ -12,7 +12,7 @@ namespace Files.App.Server; unsafe partial class Helpers { [UnmanagedCallersOnly(CallConvs = [typeof(CallConvStdcall)])] - public static HRESULT GetActivationFactory(HSTRING activatableClassId, IActivationFactory** factory) + public static HRESULT GetActivationFactory(HSTRING activatableClassId, IActivationFactory_unmanaged** factory) { if (activatableClassId.IsNull || factory is null) { @@ -21,7 +21,7 @@ public static HRESULT GetActivationFactory(HSTRING activatableClassId, IActivati try { - *factory = (IActivationFactory*)Module.GetActivationFactory(MarshalString.FromAbi((IntPtr)activatableClassId)); + *factory = (IActivationFactory_unmanaged*)Module.GetActivationFactory(MarshalString.FromAbi((IntPtr)activatableClassId)); return *factory is null ? HRESULT.CLASS_E_CLASSNOTAVAILABLE : HRESULT.S_OK; } catch (Exception e) @@ -30,4 +30,4 @@ public static HRESULT GetActivationFactory(HSTRING activatableClassId, IActivati return (HRESULT)ExceptionHelpers.GetHRForException(e); } } -} \ No newline at end of file +} diff --git a/src/Files.App.Server/Program.cs b/src/Files.App.Server/Program.cs index b00061eaba85..b31c542a3340 100644 --- a/src/Files.App.Server/Program.cs +++ b/src/Files.App.Server/Program.cs @@ -1,4 +1,4 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. using Files.Shared.Helpers; @@ -42,13 +42,13 @@ static async Task Main() unsafe { - delegate* unmanaged[Stdcall][] callbacks = new delegate* unmanaged[Stdcall][classIds.Length]; + delegate* unmanaged[Stdcall][] callbacks = new delegate* unmanaged[Stdcall][classIds.Length]; for (int i = 0; i < callbacks.Length; i++) { callbacks[i] = &Helpers.GetActivationFactory; } - fixed (delegate* unmanaged[Stdcall]* pCallbacks = callbacks) + fixed (delegate* unmanaged[Stdcall]* pCallbacks = callbacks) { if (PInvoke.RoRegisterActivationFactories(classIds, pCallbacks, out cookie) is HRESULT hr && hr.Value != 0) { diff --git a/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Icon.cs b/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Icon.cs index 8963423d924f..4c7f06879748 100644 --- a/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Icon.cs +++ b/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Icon.cs @@ -1,4 +1,4 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // SPDX-License-Identifier: MPL-2.0 using System.Collections.Concurrent; @@ -44,14 +44,12 @@ public unsafe static HRESULT TryGetThumbnail(this IWindowsStorable storable, int { thumbnailData = null; - using ComPtr pShellItemImageFactory = default; - storable.ThisPtr->QueryInterface(IID.IID_IShellItemImageFactory, (void**)pShellItemImageFactory.GetAddressOf()); - if (pShellItemImageFactory.IsNull) + if (storable.ThisPtr is not IShellItemImageFactory pShellItemImageFactory) return HRESULT.E_NOINTERFACE; // Get HBITMAP HBITMAP hBitmap = default; - HRESULT hr = pShellItemImageFactory.Get()->GetImage(new(size, size), options, &hBitmap); + HRESULT hr = pShellItemImageFactory.GetImage(new(size, size), options, &hBitmap); if (hr.ThrowIfFailedOnDebug().Failed) { if (!hBitmap.IsNull) PInvoke.DeleteObject(hBitmap); @@ -168,22 +166,21 @@ public unsafe static bool TryConvertGpBitmapToByteArray(GpBitmap* gpBitmap, out encoder = GetEncoderClsid(format); } - using ComPtr pStream = default; - HRESULT hr = PInvoke.CreateStreamOnHGlobal(HGLOBAL.Null, true, pStream.GetAddressOf()); + HRESULT hr = PInvoke.CreateStreamOnHGlobal(HGLOBAL.Null, true, out IStream pStream); if (hr.ThrowIfFailedOnDebug().Failed) { if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap); return false; } - if (PInvoke.GdipSaveImageToStream((GpImage*)gpBitmap, pStream.Get(), &encoder, (EncoderParameters*)null) is not Status.Ok) + if (PInvoke.GdipSaveImageToStream((GpImage*)gpBitmap, pStream, &encoder, (EncoderParameters*)null) is not Status.Ok) { if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap); return false; } STATSTG stat = default; - hr = pStream.Get()->Stat(&stat, (uint)STATFLAG.STATFLAG_NONAME); + hr = pStream.Stat(out stat, STATFLAG.STATFLAG_NONAME); if (hr.ThrowIfFailedOnDebug().Failed) { if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap); @@ -193,8 +190,8 @@ public unsafe static bool TryConvertGpBitmapToByteArray(GpBitmap* gpBitmap, out ulong statSize = stat.cbSize & 0xFFFFFFFF; byte* RawThumbnailData = (byte*)NativeMemory.Alloc((nuint)statSize); - pStream.Get()->Seek(0L, (SystemIO.SeekOrigin)STREAM_SEEK.STREAM_SEEK_SET, null); - hr = pStream.Get()->Read(RawThumbnailData, (uint)statSize); + pStream.Seek(0L, System.IO.SeekOrigin.Begin); + hr = pStream.Read(RawThumbnailData, (uint)statSize); if (hr.ThrowIfFailedOnDebug().Failed) { if (gpBitmap is not null) PInvoke.GdipDisposeImage((GpImage*)gpBitmap); @@ -266,14 +263,12 @@ public unsafe static HRESULT TrySetShortcutIcon(this IWindowsStorable storable, if (iconFile.ToString() is not { } iconFilePath) return HRESULT.E_INVALIDARG; - using ComPtr pShellLink = default; - - HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IShellLinkW, (void**)pShellLink.GetAddressOf()); + HRESULT hr = storable.ThisPtr.BindToHandler(null, PInvoke.BHID_SFUIObject, out IShellLinkW? pShellLink); if (hr.ThrowIfFailedOnDebug().Failed) return hr; fixed (char* pszIconFilePath = iconFilePath) - hr = pShellLink.Get()->SetIconLocation(iconFilePath, index); + hr = pShellLink!.SetIconLocation(iconFilePath, index); if (hr.ThrowIfFailedOnDebug().Failed) return hr; diff --git a/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Shell.cs b/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Shell.cs index 56755fcd59f2..e667b5024590 100644 --- a/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Shell.cs +++ b/src/Files.App.Storage/Windows/Helpers/WindowsStorableHelpers.Shell.cs @@ -1,4 +1,4 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // SPDX-License-Identifier: MPL-2.0 using System.Runtime.CompilerServices; @@ -19,26 +19,32 @@ public unsafe static partial class WindowsStorableHelpers { public static HRESULT GetPropertyValue(this IWindowsStorable storable, string propKey, out TValue value) { - using ComPtr pShellItem2 = default; - HRESULT hr = storable.ThisPtr->QueryInterface(IID.IID_IShellItem2, (void**)pShellItem2.GetAddressOf()); + if (storable.ThisPtr is not IShellItem2 pShellItem2) + { + value = default!; + return HRESULT.E_NOINTERFACE; + } - PROPERTYKEY propertyKey = default; - fixed (char* pszPropertyKey = propKey) - hr = PInvoke.PSGetPropertyKeyFromName(pszPropertyKey, &propertyKey); + HRESULT hr = PInvoke.PSGetPropertyKeyFromName(propKey, out PROPERTYKEY propertyKey); + if (hr.ThrowIfFailedOnDebug().Failed) + { + value = default!; + return hr; + } if (typeof(TValue) == typeof(string)) { - ComHeapPtr szPropertyValue = default; - hr = pShellItem2.Get()->GetString(&propertyKey, szPropertyValue.Get()); - value = (TValue)(object)szPropertyValue.Get()->ToString(); + hr = pShellItem2.GetString(propertyKey, out PWSTR szPropertyValue); + value = (TValue)(object)szPropertyValue.ToString(); + PInvoke.CoTaskMemFree(szPropertyValue); return hr; } if (typeof(TValue) == typeof(bool)) { - bool fPropertyValue = false; - hr = pShellItem2.Get()->GetBool(&propertyKey, (BOOL*)&fPropertyValue); - value = Unsafe.As(ref fPropertyValue); + hr = pShellItem2.GetBool(propertyKey, out BOOL fPropertyValue); + bool propertyValue = fPropertyValue; + value = Unsafe.As(ref propertyValue); return hr; } @@ -51,27 +57,30 @@ public static HRESULT GetPropertyValue(this IWindowsStorable storable, s public static bool HasShellAttributes(this IWindowsStorable storable, SFGAO_FLAGS attributes) { - return storable.ThisPtr->GetAttributes(attributes, out var dwRetAttributes).Succeeded && dwRetAttributes == attributes; + return storable.ThisPtr.GetAttributes(attributes, out var dwRetAttributes).Succeeded && dwRetAttributes == attributes; } public static string GetDisplayName(this IWindowsStorable storable, SIGDN options = SIGDN.SIGDN_FILESYSPATH) { - using ComHeapPtr pszName = default; - 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 + HRESULT hr = storable.ThisPtr.GetDisplayName(options, out PWSTR pszName); + string name = hr.ThrowIfFailedOnDebug().Succeeded + ? pszName.ToString() : string.Empty; + PInvoke.CoTaskMemFree(pszName); + + return name; } public static HRESULT TryInvokeContextMenuVerb(this IWindowsStorable storable, string verbName) { Debug.Assert(Thread.CurrentThread.GetApartmentState() is ApartmentState.STA); - using ComPtr pContextMenu = default; - HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf()); + HRESULT hr = storable.ThisPtr.BindToHandler(null, PInvoke.BHID_SFUIObject, out IContextMenu? pContextMenu); + if (hr.ThrowIfFailedOnDebug().Failed || pContextMenu is null) + return hr; + HMENU hMenu = PInvoke.CreatePopupMenu(); - hr = pContextMenu.Get()->QueryContextMenu(hMenu, 0, 1, 0x7FFF, PInvoke.CMF_OPTIMIZEFORINVOKE); + hr = pContextMenu.QueryContextMenu(hMenu, 0, 1, 0x7FFF, PInvoke.CMF_OPTIMIZEFORINVOKE); CMINVOKECOMMANDINFO cmici = default; cmici.cbSize = (uint)sizeof(CMINVOKECOMMANDINFO); @@ -80,7 +89,7 @@ public static HRESULT TryInvokeContextMenuVerb(this IWindowsStorable storable, s fixed (byte* pszVerbName = Encoding.ASCII.GetBytes(verbName)) { cmici.lpVerb = new(pszVerbName); - hr = pContextMenu.Get()->InvokeCommand(cmici); + hr = pContextMenu.InvokeCommand(cmici); if (!PInvoke.DestroyMenu(hMenu)) return HRESULT.E_FAIL; @@ -93,43 +102,52 @@ public static HRESULT TryInvokeContextMenuVerbs(this IWindowsStorable storable, { Debug.Assert(Thread.CurrentThread.GetApartmentState() is ApartmentState.STA); - using ComPtr pContextMenu = default; - HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf()); + HRESULT hr = storable.ThisPtr.BindToHandler(null, PInvoke.BHID_SFUIObject, out IContextMenu? pContextMenu); + if (hr.ThrowIfFailedOnDebug().Failed || pContextMenu is null) + return hr; + HMENU hMenu = PInvoke.CreatePopupMenu(); - hr = pContextMenu.Get()->QueryContextMenu(hMenu, 0, 1, 0x7FFF, PInvoke.CMF_OPTIMIZEFORINVOKE); + HRESULT result = HRESULT.S_OK; + try + { + hr = pContextMenu.QueryContextMenu(hMenu, 0, 1, 0x7FFF, PInvoke.CMF_OPTIMIZEFORINVOKE); + result = hr; - CMINVOKECOMMANDINFO cmici = default; - cmici.cbSize = (uint)sizeof(CMINVOKECOMMANDINFO); - cmici.nShow = (int)SHOW_WINDOW_CMD.SW_HIDE; + CMINVOKECOMMANDINFO cmici = default; + cmici.cbSize = (uint)sizeof(CMINVOKECOMMANDINFO); + cmici.nShow = (int)SHOW_WINDOW_CMD.SW_HIDE; - foreach (var verbName in verbNames) - { - fixed (byte* pszVerbName = Encoding.ASCII.GetBytes(verbName)) + foreach (var verbName in verbNames) { - cmici.lpVerb = new(pszVerbName); - hr = pContextMenu.Get()->InvokeCommand(cmici); - - if (!PInvoke.DestroyMenu(hMenu)) - return HRESULT.E_FAIL; + fixed (byte* pszVerbName = Encoding.ASCII.GetBytes(verbName)) + { + cmici.lpVerb = new(pszVerbName); + hr = pContextMenu.InvokeCommand(cmici); + result = hr; - if (hr.Succeeded && earlyReturnOnSuccess) - return hr; + if (hr.Succeeded && earlyReturnOnSuccess) + break; + } } } + finally + { + if (!PInvoke.DestroyMenu(hMenu)) + result = HRESULT.E_FAIL; + } - return hr; + return result; } public static HRESULT TryGetShellTooltip(this IWindowsStorable storable, out string? tooltip) { tooltip = null; - using ComPtr pQueryInfo = default; - HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IQueryInfo, (void**)pQueryInfo.GetAddressOf()); - if (hr.ThrowIfFailedOnDebug().Failed) + HRESULT hr = storable.ThisPtr.BindToHandler(null, PInvoke.BHID_SFUIObject, out IQueryInfo? pQueryInfo); + if (hr.ThrowIfFailedOnDebug().Failed || pQueryInfo is null) return hr; - pQueryInfo.Get()->GetInfoTip((uint)QITIPF_FLAGS.QITIPF_DEFAULT, out var pszTip); + hr = pQueryInfo.GetInfoTip(QITIPF_FLAGS.QITIPF_DEFAULT, out var pszTip); if (hr.ThrowIfFailedOnDebug().Failed) return hr; @@ -143,27 +161,22 @@ public static HRESULT TryPinFolderToQuickAccess(this IWindowsFolder @this) { HRESULT hr = default; - using ComPtr pExecuteCommand = default; - using ComPtr pObjectWithSelection = default; - - hr = PInvoke.CoCreateInstance(CLSID.CLSID_PinToFrequentExecute, null, CLSCTX.CLSCTX_INPROC_SERVER, IID.IID_IExecuteCommand, (void**)pExecuteCommand.GetAddressOf()); - if (hr.ThrowIfFailedOnDebug().Failed) + hr = PInvoke.CoCreateInstance(CLSID.CLSID_PinToFrequentExecute, null, CLSCTX.CLSCTX_INPROC_SERVER, out IExecuteCommand? pExecuteCommand); + if (hr.ThrowIfFailedOnDebug().Failed || pExecuteCommand is null) return hr; - using ComPtr pShellItemArray = default; - hr = PInvoke.SHCreateShellItemArrayFromShellItem(@this.ThisPtr, IID.IID_IShellItemArray, (void**)pShellItemArray.GetAddressOf()); + hr = PInvoke.SHCreateShellItemArrayFromShellItem(@this.ThisPtr, out IShellItemArray pShellItemArray); if (hr.ThrowIfFailedOnDebug().Failed) return hr; - hr = pExecuteCommand.Get()->QueryInterface(IID.IID_IObjectWithSelection, (void**)pObjectWithSelection.GetAddressOf()); - if (hr.ThrowIfFailedOnDebug().Failed) - return hr; + if (pExecuteCommand is not IObjectWithSelection pObjectWithSelection) + return HRESULT.E_NOINTERFACE; - hr = pObjectWithSelection.Get()->SetSelection(pShellItemArray.Get()); + hr = pObjectWithSelection.SetSelection(pShellItemArray); if (hr.ThrowIfFailedOnDebug().Failed) return hr; - hr = pExecuteCommand.Get()->Execute(); + hr = pExecuteCommand.Execute(); if (hr.ThrowIfFailedOnDebug().Failed) return hr; @@ -174,27 +187,22 @@ public static HRESULT TryUnpinFolderFromQuickAccess(this IWindowsFolder @this) { HRESULT hr = default; - using ComPtr pExecuteCommand = default; - using ComPtr pObjectWithSelection = default; - - hr = PInvoke.CoCreateInstance(CLSID.CLSID_UnPinFromFrequentExecute, null, CLSCTX.CLSCTX_INPROC_SERVER, IID.IID_IExecuteCommand, (void**)pExecuteCommand.GetAddressOf()); - if (hr.ThrowIfFailedOnDebug().Failed) + hr = PInvoke.CoCreateInstance(CLSID.CLSID_UnPinFromFrequentExecute, null, CLSCTX.CLSCTX_INPROC_SERVER, out IExecuteCommand? pExecuteCommand); + if (hr.ThrowIfFailedOnDebug().Failed || pExecuteCommand is null) return hr; - using ComPtr pShellItemArray = default; - hr = PInvoke.SHCreateShellItemArrayFromShellItem(@this.ThisPtr, IID.IID_IShellItemArray, (void**)pShellItemArray.GetAddressOf()); + hr = PInvoke.SHCreateShellItemArrayFromShellItem(@this.ThisPtr, out IShellItemArray pShellItemArray); if (hr.ThrowIfFailedOnDebug().Failed) return hr; - hr = pExecuteCommand.Get()->QueryInterface(IID.IID_IObjectWithSelection, (void**)pObjectWithSelection.GetAddressOf()); - if (hr.ThrowIfFailedOnDebug().Failed) - return hr; + if (pExecuteCommand is not IObjectWithSelection pObjectWithSelection) + return HRESULT.E_NOINTERFACE; - hr = pObjectWithSelection.Get()->SetSelection(pShellItemArray.Get()); + hr = pObjectWithSelection.SetSelection(pShellItemArray); if (hr.ThrowIfFailedOnDebug().Failed) return hr; - hr = pExecuteCommand.Get()->Execute(); + hr = pExecuteCommand.Execute(); if (hr.ThrowIfFailedOnDebug().Failed) return hr; @@ -205,42 +213,33 @@ public static IEnumerable GetShellNewItems(this IWindows { HRESULT hr = default; - IContextMenu* pNewMenu = default; - using ComPtr pShellExtInit = default; - using ComPtr pContextMenu2 = default; - - hr = PInvoke.CoCreateInstance(CLSID.CLSID_NewMenu, null, CLSCTX.CLSCTX_INPROC_SERVER, IID.IID_IContextMenu, (void**)&pNewMenu); - if (hr.ThrowIfFailedOnDebug().Failed) - return []; - - hr = pNewMenu->QueryInterface(IID.IID_IContextMenu2, (void**)pContextMenu2.GetAddressOf()); - if (hr.ThrowIfFailedOnDebug().Failed) + hr = PInvoke.CoCreateInstance(CLSID.CLSID_NewMenu, null, CLSCTX.CLSCTX_INPROC_SERVER, out IContextMenu? pNewMenu); + if (hr.ThrowIfFailedOnDebug().Failed || pNewMenu is null) return []; - hr = pNewMenu->QueryInterface(IID.IID_IShellExtInit, (void**)pShellExtInit.GetAddressOf()); - if (hr.ThrowIfFailedOnDebug().Failed) + if (pNewMenu is not IContextMenu2 pContextMenu2 || + pNewMenu is not IShellExtInit pShellExtInit) return []; @this.ShellNewMenu = pNewMenu; - ITEMIDLIST* pFolderPidl = default; - hr = PInvoke.SHGetIDListFromObject((IUnknown*)@this.ThisPtr, &pFolderPidl); + hr = PInvoke.SHGetIDListFromObject(@this.ThisPtr, out ITEMIDLIST* pFolderPidl); if (hr.ThrowIfFailedOnDebug().Failed) return []; - hr = pShellExtInit.Get()->Initialize(pFolderPidl, null, default); + hr = pShellExtInit.Initialize(pFolderPidl, null!, default); if (hr.ThrowIfFailedOnDebug().Failed) return []; // Inserts "New (&W)" HMENU hMenu = PInvoke.CreatePopupMenu(); - hr = pNewMenu->QueryContextMenu(hMenu, 0, 1, 256, 0); + hr = pNewMenu.QueryContextMenu(hMenu, 0, 1, 256, 0); if (hr.ThrowIfFailedOnDebug().Failed) return []; // Invokes CNewMenu::_InitMenuPopup(), which populates the hSubMenu HMENU hSubMenu = PInvoke.GetSubMenu(hMenu, 0); - hr = pContextMenu2.Get()->HandleMenuMsg(PInvoke.WM_INITMENUPOPUP, (WPARAM)(nuint)hSubMenu.Value, 0); + hr = pContextMenu2.HandleMenuMsg(PInvoke.WM_INITMENUPOPUP, (WPARAM)(nuint)hSubMenu.Value, 0); if (hr.ThrowIfFailedOnDebug().Failed) return []; @@ -280,10 +279,8 @@ public static bool InvokeShellNewItem(this IWindowsFolder @this, WindowsContextM if (@this.ShellNewMenu is null) { - IContextMenu* pNewMenu = default; - - hr = PInvoke.CoCreateInstance(CLSID.CLSID_NewMenu, null, CLSCTX.CLSCTX_INPROC_SERVER, IID.IID_IContextMenu, (void**)&pNewMenu); - if (hr.ThrowIfFailedOnDebug().Failed) + hr = PInvoke.CoCreateInstance(CLSID.CLSID_NewMenu, null, CLSCTX.CLSCTX_INPROC_SERVER, out IContextMenu? pNewMenu); + if (hr.ThrowIfFailedOnDebug().Failed || pNewMenu is null) return false; @this.ShellNewMenu = pNewMenu; @@ -294,7 +291,7 @@ public static bool InvokeShellNewItem(this IWindowsFolder @this, WindowsContextM cmici.lpVerb = (PCSTR)(byte*)item.Id; cmici.nShow = (int)SHOW_WINDOW_CMD.SW_SHOWNORMAL; - hr = @this.ShellNewMenu->InvokeCommand(&cmici); + hr = @this.ShellNewMenu.InvokeCommand(cmici); if (hr.ThrowIfFailedOnDebug().Failed) return false; diff --git a/src/Files.App.Storage/Windows/IWindowsFolder.cs b/src/Files.App.Storage/Windows/IWindowsFolder.cs index b01e1df69b15..99116eb9da47 100644 --- a/src/Files.App.Storage/Windows/IWindowsFolder.cs +++ b/src/Files.App.Storage/Windows/IWindowsFolder.cs @@ -1,15 +1,15 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // SPDX-License-Identifier: MPL-2.0 using Windows.Win32.UI.Shell; namespace Files.App.Storage { - public unsafe interface IWindowsFolder : IWindowsStorable, IChildFolder + public interface IWindowsFolder : IWindowsStorable, IChildFolder { /// /// Gets or sets the cached for the ShellNew context menu. /// - public IContextMenu* ShellNewMenu { get; set; } + public IContextMenu? ShellNewMenu { get; set; } } } diff --git a/src/Files.App.Storage/Windows/IWindowsStorable.cs b/src/Files.App.Storage/Windows/IWindowsStorable.cs index 218e3fdce2c0..de5396473e60 100644 --- a/src/Files.App.Storage/Windows/IWindowsStorable.cs +++ b/src/Files.App.Storage/Windows/IWindowsStorable.cs @@ -1,14 +1,14 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // SPDX-License-Identifier: MPL-2.0 using Windows.Win32.UI.Shell; namespace Files.App.Storage { - public unsafe interface IWindowsStorable : IStorableChild, IEquatable, IDisposable + public interface IWindowsStorable : IStorableChild, IEquatable, IDisposable { - IShellItem* ThisPtr { get; set; } + IShellItem ThisPtr { get; set; } - IContextMenu* ContextMenu { get; set; } + IContextMenu? ContextMenu { get; set; } } } diff --git a/src/Files.App.Storage/Windows/Managers/SystemTrayManager.cs b/src/Files.App.Storage/Windows/Managers/SystemTrayManager.cs index 42cc69d9a4a7..75dcf74ed543 100644 --- a/src/Files.App.Storage/Windows/Managers/SystemTrayManager.cs +++ b/src/Files.App.Storage/Windows/Managers/SystemTrayManager.cs @@ -6,6 +6,7 @@ using Windows.Win32.Foundation; using Windows.Win32.UI.Shell; using Windows.Win32.UI.WindowsAndMessaging; +using WNDPROC = Windows.Win32.Extras.ManagedWNDPROC; namespace Files.App.Storage { diff --git a/src/Files.App.Storage/Windows/Managers/TaskbarManager.cs b/src/Files.App.Storage/Windows/Managers/TaskbarManager.cs index e0d519f07250..e97f8b8785c6 100644 --- a/src/Files.App.Storage/Windows/Managers/TaskbarManager.cs +++ b/src/Files.App.Storage/Windows/Managers/TaskbarManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // SPDX-License-Identifier: MPL-2.0 using Windows.Win32; @@ -10,7 +10,7 @@ namespace Files.App.Storage { public unsafe class TaskbarManager : IDisposable { - private ComPtr pTaskbarList = default; + private ITaskbarList3? taskbarList; private static TaskbarManager? _Default = null; public static TaskbarManager Default { get; } = _Default ??= new TaskbarManager(); @@ -18,31 +18,27 @@ public unsafe class TaskbarManager : IDisposable public TaskbarManager() { Guid CLSID_TaskbarList = typeof(TaskbarList).GUID; - Guid IID_ITaskbarList3 = ITaskbarList3.IID_Guid; - HRESULT hr = PInvoke.CoCreateInstance( - &CLSID_TaskbarList, - null, - CLSCTX.CLSCTX_INPROC_SERVER, - &IID_ITaskbarList3, - (void**)pTaskbarList.GetAddressOf()); + HRESULT hr = PInvoke.CoCreateInstance(CLSID_TaskbarList, null, CLSCTX.CLSCTX_INPROC_SERVER, out ITaskbarList3? pTaskbarList); if (hr.ThrowIfFailedOnDebug().Succeeded) - hr = pTaskbarList.Get()->HrInit().ThrowIfFailedOnDebug(); + hr = pTaskbarList!.HrInit().ThrowIfFailedOnDebug(); + + taskbarList = pTaskbarList; } public HRESULT SetProgressValue(HWND hwnd, ulong ullCompleted, ulong ullTotal) { - return pTaskbarList.Get()->SetProgressValue(hwnd, ullCompleted, ullTotal); + return taskbarList?.SetProgressValue(hwnd, ullCompleted, ullTotal) ?? HRESULT.E_FAIL; } public HRESULT SetProgressState(HWND hwnd, TBPFLAG tbpFlags) { - return pTaskbarList.Get()->SetProgressState(hwnd, tbpFlags); + return taskbarList?.SetProgressState(hwnd, tbpFlags) ?? HRESULT.E_FAIL; } public void Dispose() { - pTaskbarList.Dispose(); + taskbarList = null; } } } diff --git a/src/Files.App.Storage/Windows/Managers/WindowsDriveManager.cs b/src/Files.App.Storage/Windows/Managers/WindowsDriveManager.cs index 216aa197ffe0..0a5083ed164f 100644 --- a/src/Files.App.Storage/Windows/Managers/WindowsDriveManager.cs +++ b/src/Files.App.Storage/Windows/Managers/WindowsDriveManager.cs @@ -20,7 +20,7 @@ public unsafe sealed class WindowsDriveManager : IDisposable private WindowsDriveManager() { - Guid computerFolderId = *FOLDERID.FOLDERID_ComputerFolder; + Guid computerFolderId = FOLDERID.FOLDERID_ComputerFolder; _folderChangeWatcher = new( computerFolderId, SHCNE_ID.SHCNE_DRIVEADD | SHCNE_ID.SHCNE_DRIVEREMOVED | SHCNE_ID.SHCNE_MEDIAINSERTED | SHCNE_ID.SHCNE_MEDIAREMOVED, diff --git a/src/Files.App.Storage/Windows/Managers/WindowsFolderChangeWatcher.cs b/src/Files.App.Storage/Windows/Managers/WindowsFolderChangeWatcher.cs index 0a6cabf011e8..87c6dd40a350 100644 --- a/src/Files.App.Storage/Windows/Managers/WindowsFolderChangeWatcher.cs +++ b/src/Files.App.Storage/Windows/Managers/WindowsFolderChangeWatcher.cs @@ -7,6 +7,7 @@ using Windows.Win32.UI.Shell; using Windows.Win32.UI.Shell.Common; using Windows.Win32.UI.WindowsAndMessaging; +using WNDPROC = Windows.Win32.Extras.ManagedWNDPROC; namespace Files.App.Storage { diff --git a/src/Files.App.Storage/Windows/Managers/WindowsObjectPicker.cs b/src/Files.App.Storage/Windows/Managers/WindowsObjectPicker.cs index a18625d3baf9..baf480000ba1 100644 --- a/src/Files.App.Storage/Windows/Managers/WindowsObjectPicker.cs +++ b/src/Files.App.Storage/Windows/Managers/WindowsObjectPicker.cs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: MPL-2.0 using Microsoft.Extensions.Logging; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; using System.Security.Principal; using Windows.Win32; using Windows.Win32.Foundation; @@ -22,9 +24,8 @@ public static unsafe class WindowsObjectPicker { Guid CLSID_DsObjectPicker = PInvoke.CLSID_DsObjectPicker; - using ComPtr pObjectPicker = default; - HRESULT hr = pObjectPicker.CoCreateInstance(&CLSID_DsObjectPicker, null, CLSCTX.CLSCTX_INPROC_SERVER); - if (hr.ThrowIfFailedOnDebug().Failed) + HRESULT hr = PInvoke.CoCreateInstance(CLSID_DsObjectPicker, null, CLSCTX.CLSCTX_INPROC_SERVER, out IDsObjectPicker? pObjectPicker); + if (hr.ThrowIfFailedOnDebug().Failed || pObjectPicker is null) return null; DSOP_SCOPE_INIT_INFO* scopeInitInfos = stackalloc DSOP_SCOPE_INIT_INFO[2]; @@ -53,17 +54,16 @@ public static unsafe class WindowsObjectPicker initInfo.cAttributesToFetch = 1; initInfo.apwzAttributeNames = attributeNames; - hr = pObjectPicker.Get()->Initialize(&initInfo); + hr = pObjectPicker.Initialize(ref initInfo); if (hr.ThrowIfFailedOnDebug().Failed) return null; } - using ComPtr pSelections = default; - hr = pObjectPicker.Get()->InvokeDialog(ownerHWnd, pSelections.GetAddressOf()); - if (hr == HRESULT.S_FALSE || hr.ThrowIfFailedOnDebug().Failed || pSelections.IsNull) + hr = pObjectPicker.InvokeDialog(ownerHWnd, out IDataObject pSelections); + if (hr == HRESULT.S_FALSE || hr.ThrowIfFailedOnDebug().Failed) return null; - return GetSelectedSid(pSelections.Get()); + return GetSelectedSid(pSelections); } private static DSOP_SCOPE_INIT_INFO CreateScopeInitInfo(uint scopeType, bool startingScope) @@ -106,11 +106,9 @@ private static DSOP_SCOPE_INIT_INFO CreateScopeInitInfo(uint scopeType, bool sta return info; } - private static string? GetSelectedSid(IDataObject* pSelections) + private static string? GetSelectedSid(IDataObject pSelections) { - uint clipboardFormat; - fixed (char* pszSelectionListFormatName = PInvoke.CFSTR_DSOP_DS_SELECTION_LIST) - clipboardFormat = PInvoke.RegisterClipboardFormat(pszSelectionListFormatName); + uint clipboardFormat = PInvoke.RegisterClipboardFormat(PInvoke.CFSTR_DSOP_DS_SELECTION_LIST); if (clipboardFormat is 0 || clipboardFormat > ushort.MaxValue) return null; @@ -124,14 +122,14 @@ private static DSOP_SCOPE_INIT_INFO CreateScopeInitInfo(uint scopeType, bool sta STGMEDIUM medium = default; medium.tymed = TYMED.TYMED_HGLOBAL; - HRESULT hr = pSelections->GetData(&format, &medium); + HRESULT hr = pSelections.GetData(format, out medium); if (hr.ThrowIfFailedOnDebug().Failed) return null; void* pvSelectionList = PInvoke.GlobalLock(medium.u.hGlobal); if (pvSelectionList is null) { - PInvoke.ReleaseStgMedium(&medium); + PInvoke.ReleaseStgMedium(ref medium); return null; } @@ -142,7 +140,7 @@ private static DSOP_SCOPE_INIT_INFO CreateScopeInitInfo(uint scopeType, bool sta return null; var selectionsAsSpan = pSelectionList->aDsSelection.AsSpan((int)pSelectionList->cItems); - fixed (DS_SELECTION* pSelection = selectionsAsSpan) + fixed (DS_SELECTION_unmanaged* pSelection = selectionsAsSpan) { if (pSelection->pvarFetchedAttributes is null) return null; @@ -153,31 +151,31 @@ private static DSOP_SCOPE_INIT_INFO CreateScopeInitInfo(uint scopeType, bool sta finally { PInvoke.GlobalUnlock(medium.u.hGlobal); - PInvoke.ReleaseStgMedium(&medium); + PInvoke.ReleaseStgMedium(ref medium); } } - private static string? GetSidString(VARIANT* objectSidVariant) + private static string? GetSidString(ComVariant* objectSidVariant) { - VARENUM variantType = objectSidVariant->Anonymous.Anonymous.vt; - if ((variantType & VARENUM.VT_ARRAY) is 0 || (variantType & VARENUM.VT_TYPEMASK) is not VARENUM.VT_UI1) + VarEnum variantType = objectSidVariant->VarType; + if ((variantType & VarEnum.VT_ARRAY) is 0 || (variantType & (VarEnum)0x0FFF) is not VarEnum.VT_UI1) return null; - SAFEARRAY* pSafeArray = (variantType & VARENUM.VT_BYREF) is not 0 - ? *objectSidVariant->Anonymous.Anonymous.Anonymous.pparray - : objectSidVariant->Anonymous.Anonymous.Anonymous.parray; + ref nint rawValue = ref objectSidVariant->GetRawDataRef(); + SAFEARRAY* pSafeArray = (variantType & VarEnum.VT_BYREF) is not 0 + ? *(SAFEARRAY**)rawValue + : (SAFEARRAY*)rawValue; if (pSafeArray is null || PInvoke.SafeArrayGetDim(pSafeArray) is not 1) return null; int lowerBound = 0, upperBound = 0; - if (PInvoke.SafeArrayGetLBound(pSafeArray, 1, &lowerBound).ThrowIfFailedOnDebug().Failed || - PInvoke.SafeArrayGetUBound(pSafeArray, 1, &upperBound).ThrowIfFailedOnDebug().Failed || + if (PInvoke.SafeArrayGetLBound(pSafeArray, 1, out lowerBound).ThrowIfFailedOnDebug().Failed || + PInvoke.SafeArrayGetUBound(pSafeArray, 1, out upperBound).ThrowIfFailedOnDebug().Failed || upperBound < lowerBound) return null; - void* pSidBytes; - if (PInvoke.SafeArrayAccessData(pSafeArray, &pSidBytes).ThrowIfFailedOnDebug().Failed || pSidBytes is null) + if (PInvoke.SafeArrayAccessData(pSafeArray, out void* pSidBytes).ThrowIfFailedOnDebug().Failed || pSidBytes is null) return null; try diff --git a/src/Files.App.Storage/Windows/WindowsBulkOperations.cs b/src/Files.App.Storage/Windows/WindowsBulkOperations.cs index 7e0f18b30b48..b3bff9141461 100644 --- a/src/Files.App.Storage/Windows/WindowsBulkOperations.cs +++ b/src/Files.App.Storage/Windows/WindowsBulkOperations.cs @@ -16,8 +16,8 @@ public unsafe partial class WindowsBulkOperations : IDisposable { // Fields - private readonly IFileOperation* _pFileOperation; - private readonly IFileOperationProgressSink* _pProgressSink; + private readonly IFileOperation _fileOperation; + private readonly IFileOperationProgressSink _progressSink; private readonly uint _progressSinkCookie; // Events @@ -68,22 +68,20 @@ public unsafe partial class WindowsBulkOperations : IDisposable /// /// Specifies the window handle that will own the file operation dialogs. /// 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) + public WindowsBulkOperations(HWND ownerHWnd = default, FILEOPERATION_FLAGS flags = FILEOPERATION_FLAGS.FOF_ALLOWUNDO | FILEOPERATION_FLAGS.FOF_NOCONFIRMMKDIR) { - IFileOperation* pFileOperation = null; - - HRESULT hr = PInvoke.CoCreateInstance(CLSID.CLSID_FileOperation, null, CLSCTX.CLSCTX_LOCAL_SERVER, IID.IID_IFileOperation, (void**)&pFileOperation); + HRESULT hr = PInvoke.CoCreateInstance(typeof(FileOperation).GUID, null, CLSCTX.CLSCTX_LOCAL_SERVER, out IFileOperation? fileOperation); hr.ThrowIfFailedOnDebug(); - _pFileOperation = pFileOperation; + _fileOperation = fileOperation!; if (ownerHWnd != default) - hr = _pFileOperation->SetOwnerWindow(ownerHWnd).ThrowIfFailedOnDebug(); + hr = _fileOperation.SetOwnerWindow(ownerHWnd).ThrowIfFailedOnDebug(); - hr = _pFileOperation->SetOperationFlags(flags).ThrowIfFailedOnDebug(); + hr = _fileOperation.SetOperationFlags(flags).ThrowIfFailedOnDebug(); - _pProgressSink = (IFileOperationProgressSink*)WindowsBulkOperationsSink.Create(this); - hr = _pFileOperation->Advise(_pProgressSink, out var progressSinkCookie).ThrowIfFailedOnDebug(); + _progressSink = new WindowsBulkOperationsSink(this); + hr = _fileOperation.Advise(_progressSink, out var progressSinkCookie).ThrowIfFailedOnDebug(); _progressSinkCookie = progressSinkCookie; } @@ -97,7 +95,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->CopyItem(targetItem.ThisPtr, destinationFolder.ThisPtr, pszCopyName, _pProgressSink); + return _fileOperation.CopyItem(targetItem.ThisPtr, destinationFolder.ThisPtr, pszCopyName, _progressSink); } /// @@ -107,7 +105,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->DeleteItem(targetItem.ThisPtr, _pProgressSink); + return _fileOperation.DeleteItem(targetItem.ThisPtr, _progressSink); } /// @@ -120,7 +118,7 @@ public unsafe HRESULT QueueDeleteOperation(WindowsStorable targetItem) public unsafe HRESULT QueueMoveOperation(WindowsStorable targetItem, WindowsFolder destinationFolder, string? newName) { fixed (char* pszNewName = newName) - return _pFileOperation->MoveItem(targetItem.ThisPtr, destinationFolder.ThisPtr, pszNewName, null); + return _fileOperation.MoveItem(targetItem.ThisPtr, destinationFolder.ThisPtr, pszNewName, null!); } /// @@ -134,7 +132,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->NewItem(destinationFolder.ThisPtr, (uint)fileAttributes, pszName, pszTemplateName, _pProgressSink); + return _fileOperation.NewItem(destinationFolder.ThisPtr, (uint)fileAttributes, pszName, pszTemplateName, _progressSink); } /// @@ -146,7 +144,7 @@ public unsafe HRESULT QueueCreateOperation(WindowsFolder destinationFolder, FILE public unsafe HRESULT QueueRenameOperation(WindowsStorable targetItem, string newName) { fixed (char* pszNewName = newName) - return _pFileOperation->RenameItem(targetItem.ThisPtr, pszNewName, _pProgressSink); + return _fileOperation.RenameItem(targetItem.ThisPtr, pszNewName, _progressSink); } /// @@ -155,7 +153,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->PerformOperations(); + return _fileOperation.PerformOperations(); } // Disposer @@ -163,11 +161,7 @@ public unsafe HRESULT PerformAllOperations() /// public unsafe void Dispose() { - if (_pProgressSink is not null) - _pFileOperation->Unadvise(_progressSinkCookie); - - _pFileOperation->Release(); - _pProgressSink->Release(); + _fileOperation.Unadvise(_progressSinkCookie); } } } diff --git a/src/Files.App.Storage/Windows/WindowsBulkOperationsSink.Methods.cs b/src/Files.App.Storage/Windows/WindowsBulkOperationsSink.Methods.cs index 542200369ecc..da105ecaf068 100644 --- a/src/Files.App.Storage/Windows/WindowsBulkOperationsSink.Methods.cs +++ b/src/Files.App.Storage/Windows/WindowsBulkOperationsSink.Methods.cs @@ -1,18 +1,27 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // SPDX-License-Identifier: MPL-2.0 using Windows.Win32.Foundation; using Windows.Win32.UI.Shell; +using System.Runtime.InteropServices.Marshalling; namespace Files.App.Storage { public sealed partial class WindowsBulkOperations : IDisposable { - private unsafe partial struct WindowsBulkOperationsSink : IFileOperationProgressSink.Interface + [GeneratedComClass] + private sealed partial class WindowsBulkOperationsSink : IFileOperationProgressSink { + private readonly WeakReference _operationsReference; + + public WindowsBulkOperationsSink(WindowsBulkOperations operations) + { + _operationsReference = new(operations); + } + public HRESULT StartOperations() { - if (_operationsHandle.Target is WindowsBulkOperations operations) + if (_operationsReference.TryGetTarget(out var operations)) { operations.OperationsStarted?.Invoke(operations, EventArgs.Empty); return HRESULT.S_OK; @@ -23,7 +32,7 @@ public HRESULT StartOperations() public HRESULT FinishOperations(HRESULT hrResult) { - if (_operationsHandle.Target is WindowsBulkOperations operations) + if (_operationsReference.TryGetTarget(out var operations)) { operations.OperationsFinished?.Invoke(operations, new(result: hrResult)); return HRESULT.S_OK; @@ -32,9 +41,9 @@ public HRESULT FinishOperations(HRESULT hrResult) return HRESULT.E_FAIL; } - public unsafe HRESULT PreRenameItem(uint dwFlags, IShellItem* pSource, PCWSTR pszNewName) + public HRESULT PreRenameItem(uint dwFlags, IShellItem pSource, PCWSTR pszNewName) { - if (_operationsHandle.Target is WindowsBulkOperations operations) + if (_operationsReference.TryGetTarget(out var operations)) { operations.ItemRenaming?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), null, null, pszNewName.ToString(), null, default)); return HRESULT.S_OK; @@ -43,9 +52,9 @@ public unsafe HRESULT PreRenameItem(uint dwFlags, IShellItem* pSource, PCWSTR ps return HRESULT.E_FAIL; } - public unsafe HRESULT PostRenameItem(uint dwFlags, IShellItem* pSource, PCWSTR pszNewName, HRESULT hrRename, IShellItem* psiNewlyCreated) + public HRESULT PostRenameItem(uint dwFlags, IShellItem pSource, PCWSTR pszNewName, HRESULT hrRename, IShellItem psiNewlyCreated) { - if (_operationsHandle.Target is WindowsBulkOperations operations) + if (_operationsReference.TryGetTarget(out var operations)) { operations.ItemRenamed?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), null, WindowsStorable.TryParse(psiNewlyCreated), pszNewName.ToString(), null, hrRename)); return HRESULT.S_OK; @@ -54,9 +63,9 @@ public unsafe HRESULT PostRenameItem(uint dwFlags, IShellItem* pSource, PCWSTR p return HRESULT.E_FAIL; } - public unsafe HRESULT PreMoveItem(uint dwFlags, IShellItem* pSource, IShellItem* psiDestinationFolder, PCWSTR pszNewName) + public HRESULT PreMoveItem(uint dwFlags, IShellItem pSource, IShellItem psiDestinationFolder, PCWSTR pszNewName) { - if (_operationsHandle.Target is WindowsBulkOperations operations) + if (_operationsReference.TryGetTarget(out var operations)) { operations.ItemMoving?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), new WindowsFolder(psiDestinationFolder), null, pszNewName.ToString(), null, default)); return HRESULT.S_OK; @@ -65,9 +74,9 @@ public unsafe HRESULT PreMoveItem(uint dwFlags, IShellItem* pSource, IShellItem* return HRESULT.E_FAIL; } - public unsafe HRESULT PostMoveItem(uint dwFlags, IShellItem* pSource, IShellItem* psiDestinationFolder, PCWSTR pszNewName, HRESULT hrMove, IShellItem* psiNewlyCreated) + public HRESULT PostMoveItem(uint dwFlags, IShellItem pSource, IShellItem psiDestinationFolder, PCWSTR pszNewName, HRESULT hrMove, IShellItem psiNewlyCreated) { - if (_operationsHandle.Target is WindowsBulkOperations operations) + if (_operationsReference.TryGetTarget(out var operations)) { operations.ItemMoved?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), new WindowsFolder(psiDestinationFolder), WindowsStorable.TryParse(psiNewlyCreated), pszNewName.ToString(), null, hrMove)); return HRESULT.S_OK; @@ -76,9 +85,9 @@ public unsafe HRESULT PostMoveItem(uint dwFlags, IShellItem* pSource, IShellItem return HRESULT.E_FAIL; } - public unsafe HRESULT PreCopyItem(uint dwFlags, IShellItem* pSource, IShellItem* psiDestinationFolder, PCWSTR pszNewName) + public HRESULT PreCopyItem(uint dwFlags, IShellItem pSource, IShellItem psiDestinationFolder, PCWSTR pszNewName) { - if (_operationsHandle.Target is WindowsBulkOperations operations) + if (_operationsReference.TryGetTarget(out var operations)) { operations.ItemCopying?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), new WindowsFolder(psiDestinationFolder), null, pszNewName.ToString(), null, default)); return HRESULT.S_OK; @@ -87,9 +96,9 @@ public unsafe HRESULT PreCopyItem(uint dwFlags, IShellItem* pSource, IShellItem* return HRESULT.E_FAIL; } - public unsafe HRESULT PostCopyItem(uint dwFlags, IShellItem* pSource, IShellItem* psiDestinationFolder, PCWSTR pszNewName, HRESULT hrCopy, IShellItem* psiNewlyCreated) + public HRESULT PostCopyItem(uint dwFlags, IShellItem pSource, IShellItem psiDestinationFolder, PCWSTR pszNewName, HRESULT hrCopy, IShellItem psiNewlyCreated) { - if (_operationsHandle.Target is WindowsBulkOperations operations) + if (_operationsReference.TryGetTarget(out var operations)) { operations.ItemCopied?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), new WindowsFolder(psiDestinationFolder), WindowsStorable.TryParse(psiNewlyCreated), pszNewName.ToString(), null, hrCopy)); return HRESULT.S_OK; @@ -98,9 +107,9 @@ public unsafe HRESULT PostCopyItem(uint dwFlags, IShellItem* pSource, IShellItem return HRESULT.E_FAIL; } - public unsafe HRESULT PreDeleteItem(uint dwFlags, IShellItem* pSource) + public HRESULT PreDeleteItem(uint dwFlags, IShellItem pSource) { - if (_operationsHandle.Target is WindowsBulkOperations operations) + if (_operationsReference.TryGetTarget(out var operations)) { operations.ItemDeleting?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), null, null, null, null, default)); return HRESULT.S_OK; @@ -109,9 +118,9 @@ public unsafe HRESULT PreDeleteItem(uint dwFlags, IShellItem* pSource) return HRESULT.E_FAIL; } - public unsafe HRESULT PostDeleteItem(uint dwFlags, IShellItem* pSource, HRESULT hrDelete, IShellItem* psiNewlyCreated) + public HRESULT PostDeleteItem(uint dwFlags, IShellItem pSource, HRESULT hrDelete, IShellItem psiNewlyCreated) { - if (_operationsHandle.Target is WindowsBulkOperations operations) + if (_operationsReference.TryGetTarget(out var operations)) { operations.ItemDeleted?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, WindowsStorable.TryParse(pSource), null, WindowsStorable.TryParse(psiNewlyCreated), null, null, hrDelete)); return HRESULT.S_OK; @@ -120,9 +129,9 @@ public unsafe HRESULT PostDeleteItem(uint dwFlags, IShellItem* pSource, HRESULT return HRESULT.E_FAIL; } - public unsafe HRESULT PreNewItem(uint dwFlags, IShellItem* psiDestinationFolder, PCWSTR pszNewName) + public HRESULT PreNewItem(uint dwFlags, IShellItem psiDestinationFolder, PCWSTR pszNewName) { - if (_operationsHandle.Target is WindowsBulkOperations operations) + if (_operationsReference.TryGetTarget(out var operations)) { operations.ItemCreating?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, null, new WindowsFolder(psiDestinationFolder), null, pszNewName.ToString(), null, default)); return HRESULT.S_OK; @@ -131,9 +140,9 @@ public unsafe HRESULT PreNewItem(uint dwFlags, IShellItem* psiDestinationFolder, return HRESULT.E_FAIL; } - public unsafe HRESULT PostNewItem(uint dwFlags, IShellItem* psiDestinationFolder, PCWSTR pszNewName, PCWSTR pszTemplateName, uint dwFileAttributes, HRESULT hrNew, IShellItem* psiNewItem) + public HRESULT PostNewItem(uint dwFlags, IShellItem psiDestinationFolder, PCWSTR pszNewName, PCWSTR pszTemplateName, uint dwFileAttributes, HRESULT hrNew, IShellItem psiNewItem) { - if (_operationsHandle.Target is WindowsBulkOperations operations) + if (_operationsReference.TryGetTarget(out var operations)) { operations.ItemCreated?.Invoke(operations, new((_TRANSFER_SOURCE_FLAGS)dwFlags, null, new WindowsFolder(psiDestinationFolder), WindowsStorable.TryParse(psiNewItem), pszNewName.ToString(), pszTemplateName.ToString(), hrNew)); return HRESULT.S_OK; @@ -144,7 +153,7 @@ public unsafe HRESULT PostNewItem(uint dwFlags, IShellItem* psiDestinationFolder public HRESULT UpdateProgress(uint iWorkTotal, uint iWorkSoFar) { - if (_operationsHandle.Target is WindowsBulkOperations operations) + if (_operationsReference.TryGetTarget(out var operations)) { var percentage = iWorkTotal is 0 ? 0 : iWorkSoFar * 100.0 / iWorkTotal; operations.ProgressUpdated?.Invoke(operations, new((int)percentage, null)); diff --git a/src/Files.App.Storage/Windows/WindowsBulkOperationsSink.VTable.cs b/src/Files.App.Storage/Windows/WindowsBulkOperationsSink.VTable.cs deleted file mode 100644 index 6a0c19647551..000000000000 --- a/src/Files.App.Storage/Windows/WindowsBulkOperationsSink.VTable.cs +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (c) Files Community -// SPDX-License-Identifier: MPL-2.0 - -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using Windows.Win32.Foundation; -using Windows.Win32.UI.Shell; -using WinRT.Interop; - -namespace Files.App.Storage -{ - public sealed partial class WindowsBulkOperations : IDisposable - { - private unsafe partial struct WindowsBulkOperationsSink - { - private static readonly void** _lpPopulatedVtbl = PopulateVTable(); - - private void** _lpVtbl; - private volatile int _refCount; - private GCHandle _operationsHandle; - - public static WindowsBulkOperationsSink* Create(WindowsBulkOperations operations) - { - WindowsBulkOperationsSink* operationsSink = (WindowsBulkOperationsSink*)NativeMemory.Alloc((nuint)sizeof(WindowsBulkOperationsSink)); - operationsSink->_lpVtbl = _lpPopulatedVtbl; - operationsSink->_refCount = 1; - operationsSink->_operationsHandle = GCHandle.Alloc(operations); - - return operationsSink; - } - - private static void** PopulateVTable() - { - void** vtbl = (void**)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(WindowsBulkOperationsSink), sizeof(void*) * 19); - vtbl[0] = (delegate* unmanaged)&Vtbl.QueryInterface; - vtbl[1] = (delegate* unmanaged)&Vtbl.AddRef; - vtbl[2] = (delegate* unmanaged)&Vtbl.Release; - vtbl[3] = (delegate* unmanaged)&Vtbl.StartOperations; - vtbl[4] = (delegate* unmanaged)&Vtbl.FinishOperations; - vtbl[5] = (delegate* unmanaged)&Vtbl.PreRenameItem; - vtbl[6] = (delegate* unmanaged)&Vtbl.PostRenameItem; - vtbl[7] = (delegate* unmanaged)&Vtbl.PreMoveItem; - vtbl[8] = (delegate* unmanaged)&Vtbl.PostMoveItem; - vtbl[9] = (delegate* unmanaged)&Vtbl.PreCopyItem; - vtbl[10] = (delegate* unmanaged)&Vtbl.PostCopyItem; - vtbl[11] = (delegate* unmanaged)&Vtbl.PreDeleteItem; - vtbl[12] = (delegate* unmanaged)&Vtbl.PostDeleteItem; - vtbl[13] = (delegate* unmanaged)&Vtbl.PreNewItem; - vtbl[14] = (delegate* unmanaged)&Vtbl.PostNewItem; - vtbl[15] = (delegate* unmanaged)&Vtbl.UpdateProgress; - vtbl[16] = (delegate* unmanaged)&Vtbl.ResetTimer; - vtbl[17] = (delegate* unmanaged)&Vtbl.PauseTimer; - vtbl[18] = (delegate* unmanaged)&Vtbl.ResumeTimer; - - return vtbl; - } - - private static class Vtbl - { - [UnmanagedCallersOnly] - public static HRESULT QueryInterface(WindowsBulkOperationsSink* @this, Guid* riid, void** ppv) - { - if (ppv is null) - return HRESULT.E_POINTER; - - if (riid->Equals(IID.IID_IUnknown) || riid->Equals(IFileOperationProgressSink.IID_Guid)) - { - Interlocked.Increment(ref @this->_refCount); - *ppv = @this; - return HRESULT.S_OK; - } - - return HRESULT.E_NOINTERFACE; - } - - [UnmanagedCallersOnly] - public static int AddRef(WindowsBulkOperationsSink* @this) - => Interlocked.Increment(ref @this->_refCount); - - [UnmanagedCallersOnly] - public static int Release(WindowsBulkOperationsSink* @this) - { - int newRefCount = Interlocked.Decrement(ref @this->_refCount); - if (newRefCount is 0) - { - if (@this->_operationsHandle.IsAllocated) - @this->_operationsHandle.Free(); - - NativeMemory.Free(@this); - } - - return newRefCount; - } - - [UnmanagedCallersOnly] - public static HRESULT StartOperations(WindowsBulkOperationsSink* @this) - => @this->StartOperations(); - - [UnmanagedCallersOnly] - public static HRESULT FinishOperations(WindowsBulkOperationsSink* @this, HRESULT hrResult) - => @this->FinishOperations(hrResult); - - [UnmanagedCallersOnly] - public static HRESULT PreRenameItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem, PCWSTR pszNewName) - => @this->PreRenameItem(dwFlags, psiItem, pszNewName); - - [UnmanagedCallersOnly] - public static HRESULT PostRenameItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem, PCWSTR pszNewName, HRESULT hrRename, IShellItem* psiNewlyCreated) - => @this->PostRenameItem(dwFlags, psiItem, pszNewName, hrRename, psiNewlyCreated); - - [UnmanagedCallersOnly] - public static HRESULT PreMoveItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, PCWSTR pszNewName) - => @this->PreMoveItem(dwFlags, psiItem, psiDestinationFolder, pszNewName); - - [UnmanagedCallersOnly] - public static HRESULT PostMoveItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, PCWSTR pszNewName, HRESULT hrMove, IShellItem* psiNewlyCreated) - => @this->PostMoveItem(dwFlags, psiItem, psiDestinationFolder, pszNewName, hrMove, psiNewlyCreated); - - [UnmanagedCallersOnly] - public static HRESULT PreCopyItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, PCWSTR pszNewName) - => @this->PreCopyItem(dwFlags, psiItem, psiDestinationFolder, pszNewName); - - [UnmanagedCallersOnly] - public static HRESULT PostCopyItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem, IShellItem* psiDestinationFolder, PCWSTR pszNewName, HRESULT hrCopy, IShellItem* psiNewlyCreated) - => @this->PostCopyItem(dwFlags, psiItem, psiDestinationFolder, pszNewName, hrCopy, psiNewlyCreated); - - [UnmanagedCallersOnly] - public static HRESULT PreDeleteItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem) - => @this->PreDeleteItem(dwFlags, psiItem); - - [UnmanagedCallersOnly] - public static HRESULT PostDeleteItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiItem, HRESULT hrDelete, IShellItem* psiNewlyCreated) - => @this->PostDeleteItem(dwFlags, psiItem, hrDelete, psiNewlyCreated); - - [UnmanagedCallersOnly] - public static HRESULT PreNewItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiDestinationFolder, PCWSTR pszNewName) - => @this->PreNewItem(dwFlags, psiDestinationFolder, pszNewName); - - [UnmanagedCallersOnly] - public static HRESULT PostNewItem(WindowsBulkOperationsSink* @this, uint dwFlags, IShellItem* psiDestinationFolder, PCWSTR pszNewName, PCWSTR pszTemplateName, uint dwFileAttributes, HRESULT hrNew, IShellItem* psiNewItem) - => @this->PostNewItem(dwFlags, psiDestinationFolder, pszNewName, pszTemplateName, dwFileAttributes, hrNew, psiNewItem); - - [UnmanagedCallersOnly] - public static HRESULT UpdateProgress(WindowsBulkOperationsSink* @this, uint iWorkTotal, uint iWorkSoFar) - => @this->UpdateProgress(iWorkTotal, iWorkSoFar); - - [UnmanagedCallersOnly] - public static HRESULT ResetTimer(WindowsBulkOperationsSink* @this) - => @this->ResetTimer(); - - [UnmanagedCallersOnly] - public static HRESULT PauseTimer(WindowsBulkOperationsSink* @this) - => @this->PauseTimer(); - - [UnmanagedCallersOnly] - public static HRESULT ResumeTimer(WindowsBulkOperationsSink* @this) - => @this->ResumeTimer(); - } - } - } -} diff --git a/src/Files.App.Storage/Windows/WindowsFile.cs b/src/Files.App.Storage/Windows/WindowsFile.cs index c8d6fb5bb52e..f78618db4728 100644 --- a/src/Files.App.Storage/Windows/WindowsFile.cs +++ b/src/Files.App.Storage/Windows/WindowsFile.cs @@ -1,4 +1,4 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // SPDX-License-Identifier: MPL-2.0 using System.IO; @@ -7,9 +7,9 @@ namespace Files.App.Storage { [DebuggerDisplay("{" + nameof(ToString) + "()}")] - public unsafe class WindowsFile : WindowsStorable, IWindowsFile + public class WindowsFile : WindowsStorable, IWindowsFile { - public WindowsFile(IShellItem* ptr) + public WindowsFile(IShellItem ptr) { ThisPtr = ptr; } diff --git a/src/Files.App.Storage/Windows/WindowsFolder.cs b/src/Files.App.Storage/Windows/WindowsFolder.cs index a54bd73e64e0..d587473715f3 100644 --- a/src/Files.App.Storage/Windows/WindowsFolder.cs +++ b/src/Files.App.Storage/Windows/WindowsFolder.cs @@ -1,4 +1,4 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // SPDX-License-Identifier: MPL-2.0 using System.Runtime.CompilerServices; @@ -13,7 +13,7 @@ namespace Files.App.Storage public unsafe class WindowsFolder : WindowsStorable, IWindowsFolder { /// - public IContextMenu* ShellNewMenu + public IContextMenu? ShellNewMenu { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; @@ -22,42 +22,38 @@ public IContextMenu* ShellNewMenu set; } - public WindowsFolder(IShellItem* ptr) + public WindowsFolder(IShellItem ptr) { ThisPtr = ptr; } public WindowsFolder(Guid folderId) { - IShellItem* pShellItem = default; - - HRESULT hr = PInvoke.SHGetKnownFolderItem(&folderId, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, IID.IID_IShellItem, (void**)&pShellItem); + HRESULT hr = PInvoke.SHGetKnownFolderItem(folderId, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, null, out IShellItem shellItem); if (hr.Failed) { - fixed (char* pszShellPath = $"Shell:::{folderId:B}") - hr = PInvoke.SHCreateItemFromParsingName(pszShellPath, null, IID.IID_IShellItem, (void**)&pShellItem); + hr = PInvoke.SHCreateItemFromParsingName($"Shell:::{folderId:B}", null, out shellItem); // Invalid FOLDERID; this should never happen. hr.ThrowOnFailure(); } - ThisPtr = pShellItem; + ThisPtr = shellItem; } public IAsyncEnumerable GetItemsAsync(StorableType type = StorableType.All, CancellationToken cancellationToken = default) { - using ComPtr pEnumShellItems = default; - - HRESULT hr = ThisPtr->BindToHandler(null, BHID.BHID_EnumItems, IID.IID_IEnumShellItems, (void**)pEnumShellItems.GetAddressOf()); + HRESULT hr = ThisPtr.BindToHandler(null, PInvoke.BHID_EnumItems, out IEnumShellItems? pEnumShellItems); if (hr.ThrowIfFailedOnDebug().Failed) return Enumerable.Empty().ToAsyncEnumerable(); List childItems = []; + IShellItem[] pChildShellItems = new IShellItem[1]; - IShellItem* pChildShellItem = null; - while ((hr = pEnumShellItems.Get()->Next(1, &pChildShellItem)) == HRESULT.S_OK) + while ((hr = pEnumShellItems!.Next(1, pChildShellItems, null)) == HRESULT.S_OK) { - bool isFolder = pChildShellItem->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var dwAttributes).Succeeded && dwAttributes is SFGAO_FLAGS.SFGAO_FOLDER; + IShellItem pChildShellItem = pChildShellItems[0]; + bool isFolder = pChildShellItem.GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var dwAttributes).Succeeded && dwAttributes is SFGAO_FLAGS.SFGAO_FOLDER; if (type.HasFlag(StorableType.File) && !isFolder) { @@ -78,8 +74,7 @@ public IAsyncEnumerable GetItemsAsync(StorableType type = Storab public override void Dispose() { base.Dispose(); - - if (ShellNewMenu is not null) ShellNewMenu->Release(); + ShellNewMenu = null; } } } diff --git a/src/Files.App.Storage/Windows/WindowsStorable.cs b/src/Files.App.Storage/Windows/WindowsStorable.cs index d7ed69cc2f31..f2df4d2dc78f 100644 --- a/src/Files.App.Storage/Windows/WindowsStorable.cs +++ b/src/Files.App.Storage/Windows/WindowsStorable.cs @@ -1,4 +1,4 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. using System.Runtime.CompilerServices; @@ -9,20 +9,20 @@ namespace Files.App.Storage { - public unsafe abstract class WindowsStorable : IWindowsStorable + public abstract class WindowsStorable : IWindowsStorable { /// - public IShellItem* ThisPtr + public IShellItem ThisPtr { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; [MethodImpl(MethodImplOptions.AggressiveInlining)] set; - } + } = null!; /// - public IContextMenu* ContextMenu + public IContextMenu? ContextMenu { [MethodImpl(MethodImplOptions.AggressiveInlining)] get; @@ -39,32 +39,29 @@ public IContextMenu* ContextMenu public static WindowsStorable? TryParse(string szPath) { - HRESULT hr = default; - IShellItem* pShellItem = null; - - fixed (char* pszPath = szPath) - hr = PInvoke.SHCreateItemFromParsingName(pszPath, null, IID.IID_IShellItem, (void**)&pShellItem); - - if (pShellItem is null) + HRESULT hr = PInvoke.SHCreateItemFromParsingName(szPath, null, out IShellItem pShellItem); + if (hr.ThrowIfFailedOnDebug().Failed) return null; return TryParse(pShellItem); } - public static WindowsStorable? TryParse(IShellItem* pShellItem) + public static WindowsStorable? TryParse(IShellItem? pShellItem) { - bool isFolder = pShellItem->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var returnedAttributes).Succeeded && returnedAttributes is SFGAO_FLAGS.SFGAO_FOLDER; + if (pShellItem is null) + return null; + + bool isFolder = pShellItem.GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var returnedAttributes).Succeeded && returnedAttributes is SFGAO_FLAGS.SFGAO_FOLDER; return isFolder ? new WindowsFolder(pShellItem) : new WindowsFile(pShellItem); } /// - public unsafe Task GetParentAsync(CancellationToken cancellationToken = default) + public Task GetParentAsync(CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); - IShellItem* pParentFolder = default; - HRESULT hr = ThisPtr->GetParent(&pParentFolder); + HRESULT hr = ThisPtr.GetParent(out var pParentFolder); if (hr.ThrowIfFailedOnDebug().Failed) return Task.FromResult(null); @@ -86,8 +83,7 @@ public override int GetHashCode() /// public virtual void Dispose() { - if (ThisPtr is not null) ThisPtr->Release(); - if (ContextMenu is not null) ContextMenu->Release(); + ContextMenu = null; } /// @@ -97,12 +93,12 @@ public override string ToString() } /// - public unsafe bool Equals(IWindowsStorable? other) + public bool Equals(IWindowsStorable? other) { if (other is null) return false; - return ThisPtr->Compare(other.ThisPtr, (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/Data/Items/WidgetRecentItem.cs b/src/Files.App/Data/Items/WidgetRecentItem.cs index ec26009caefb..e5ae7dee93eb 100644 --- a/src/Files.App/Data/Items/WidgetRecentItem.cs +++ b/src/Files.App/Data/Items/WidgetRecentItem.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using Microsoft.UI.Xaml.Media.Imaging; -using Windows.Win32; using Windows.Win32.UI.Shell; namespace Files.App.Data.Items @@ -38,7 +37,7 @@ public BitmapImage? Icon /// /// This has to be removed in the future. /// - public unsafe required ComPtr ShellItem { get; init; } + public required IShellItem ShellItem { get; init; } /// /// Loads thumbnail icon of the recent item. @@ -57,9 +56,8 @@ public async Task LoadRecentItemIconAsync() public override bool Equals(object? other) => other is RecentItem item && Equals(item); public bool Equals(RecentItem? other) => other is not null && other.Name == Name && other.Path == Path; - public unsafe void Dispose() + public void Dispose() { - ShellItem.Dispose(); } } } diff --git a/src/Files.App/Data/Items/WindowEx.cs b/src/Files.App/Data/Items/WindowEx.cs index 6ff6709a8248..f17ad3a9cab0 100644 --- a/src/Files.App/Data/Items/WindowEx.cs +++ b/src/Files.App/Data/Items/WindowEx.cs @@ -11,6 +11,8 @@ using Windows.Win32.Foundation; using Windows.Win32.Graphics.Gdi; using Windows.Win32.UI.WindowsAndMessaging; +using MONITORENUMPROC = Windows.Win32.Extras.ManagedMONITORENUMPROC; +using WNDPROC = Windows.Win32.Extras.ManagedWNDPROC; namespace Files.App.Data.Items { diff --git a/src/Files.App/Services/Storage/StorageTrashBinService.cs b/src/Files.App/Services/Storage/StorageTrashBinService.cs index cfbbc32e19b4..978ea9a4b7d5 100644 --- a/src/Files.App/Services/Storage/StorageTrashBinService.cs +++ b/src/Files.App/Services/Storage/StorageTrashBinService.cs @@ -130,38 +130,64 @@ public async Task RestoreAllTrashesAsync() private unsafe bool RestoreAllTrashesInternal() { // Get IShellItem for Recycle Bin folder - using ComPtr pRecycleBinFolderShellItem = default; - HRESULT hr = PInvoke.SHGetKnownFolderItem(FOLDERID.FOLDERID_RecycleBinFolder, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, IID.IID_IShellItem, (void**)pRecycleBinFolderShellItem.GetAddressOf()); + HRESULT hr = PInvoke.SHGetKnownFolderItem(FOLDERID.FOLDERID_RecycleBinFolder, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, null, out IShellItem pRecycleBinFolderShellItem); + if (hr.ThrowIfFailedOnDebug().Failed) + return false; // Get IEnumShellItems for Recycle Bin folder - using ComPtr pEnumShellItems = default; - hr = pRecycleBinFolderShellItem.Get()->BindToHandler(null, BHID.BHID_EnumItems, IID.IID_IEnumShellItems, (void**)pEnumShellItems.GetAddressOf()); + hr = pRecycleBinFolderShellItem.BindToHandler(null, PInvoke.BHID_EnumItems, out IEnumShellItems? pEnumShellItems); + if (hr.ThrowIfFailedOnDebug().Failed || pEnumShellItems is null) + return false; // Initialize how to perform the operation - using ComPtr pFileOperation = default; - hr = PInvoke.CoCreateInstance(CLSID.CLSID_FileOperation, null, CLSCTX.CLSCTX_LOCAL_SERVER, IID.IID_IFileOperation, (void**)pFileOperation.GetAddressOf()); - hr = pFileOperation.Get()->SetOperationFlags(FILEOPERATION_FLAGS.FOF_NO_UI); - hr = pFileOperation.Get()->SetOwnerWindow(new(MainWindow.Instance.WindowHandle)); + hr = PInvoke.CoCreateInstance(typeof(FileOperation).GUID, null, CLSCTX.CLSCTX_LOCAL_SERVER, out IFileOperation? pFileOperation); + if (hr.ThrowIfFailedOnDebug().Failed || pFileOperation is null) + return false; + + hr = pFileOperation.SetOperationFlags(FILEOPERATION_FLAGS.FOF_NO_UI); + if (hr.ThrowIfFailedOnDebug().Failed) + return false; - using ComPtr pShellItem = default; - while (pEnumShellItems.Get()->Next(1, pShellItem.GetAddressOf()) == HRESULT.S_OK) + hr = pFileOperation.SetOwnerWindow(new(MainWindow.Instance.WindowHandle)); + if (hr.ThrowIfFailedOnDebug().Failed) + return false; + + IShellItem[] shellItems = new IShellItem[1]; + while (pEnumShellItems.Next(1, shellItems, null) == HRESULT.S_OK) { - // Get the original path - using ComPtr pShellItem2 = default; - hr = pShellItem.Get()->QueryInterface(IID.IID_IShellItem2, (void**)pShellItem2.GetAddressOf()); + IShellItem shellItem = shellItems[0]; + + if (shellItem is not IShellItem2 shellItem2) + continue; + hr = PInvoke.PSGetPropertyKeyFromName("System.Recycle.DeletedFrom", out var originalPathPropertyKey); - hr = pShellItem2.Get()->GetString(originalPathPropertyKey, out var szOriginalPath); + if (hr.Failed) + continue; + + hr = shellItem2.GetString(originalPathPropertyKey, out var szOriginalPath); + if (hr.Failed) + continue; - // Get IShellItem of the original path - hr = PInvoke.SHCreateItemFromParsingName(szOriginalPath.ToString(), null, typeof(IShellItem).GUID, out var pOriginalPathShellItemPtr); - var pOriginalPathShellItem = (IShellItem*)pOriginalPathShellItemPtr; + try + { + // Get IShellItem of the original path + hr = PInvoke.SHCreateItemFromParsingName(szOriginalPath.ToString(), null, out IShellItem originalPathShellItem); + if (hr.Failed) + continue; - // Define the shell item to restore - hr = pFileOperation.Get()->MoveItem(pShellItem.Get(), pOriginalPathShellItem, default(PCWSTR), null); + // Define the shell item to restore + hr = pFileOperation.MoveItem(shellItem, originalPathShellItem, null!, null!); + } + finally + { + PInvoke.CoTaskMemFree(szOriginalPath); + } } // Perform - hr = pFileOperation.Get()->PerformOperations(); + hr = pFileOperation.PerformOperations(); + if (hr.ThrowIfFailedOnDebug().Failed) + return false; // Reset the icon PInvoke.SHUpdateRecycleBinIcon(); diff --git a/src/Files.App/Services/Windows/WindowsDialogService.cs b/src/Files.App/Services/Windows/WindowsDialogService.cs index 20612523bd0a..93a406aa52f6 100644 --- a/src/Files.App/Services/Windows/WindowsDialogService.cs +++ b/src/Files.App/Services/Windows/WindowsDialogService.cs @@ -1,4 +1,4 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. using Microsoft.Extensions.Logging; @@ -22,11 +22,10 @@ public unsafe bool Open_FileOpenDialog(nint hWnd, bool pickFoldersOnly, string[] try { - using ComPtr pDialog = default; - HRESULT hr = pDialog.CoCreateInstance(CLSID.CLSID_FileOpenDialog, null, CLSCTX.CLSCTX_INPROC_SERVER); + HRESULT hr = PInvoke.CoCreateInstance(typeof(FileOpenDialog).GUID, null, CLSCTX.CLSCTX_INPROC_SERVER, out IFileOpenDialog? pDialog); // Handle COM creation failure gracefully - if (hr.Failed) + if (hr.Failed || pDialog is null) { App.Logger.LogError("Failed to create IFileOpenDialog COM object. HRESULT: 0x{0:X8}", hr.Value); return false; @@ -48,40 +47,37 @@ public unsafe bool Open_FileOpenDialog(nint hWnd, bool pickFoldersOnly, string[] } // Set the file type using the extension list - pDialog.Get()->SetFileTypes(extensions.ToArray()); + pDialog.SetFileTypes(extensions.ToArray()); } // Get the default shell folder (My Computer) - using ComPtr pDefaultFolderShellItem = default; - fixed (char* pszDefaultFolderPath = Environment.GetFolderPath(defaultFolder)) + IShellItem? pDefaultFolderShellItem = null; + hr = PInvoke.SHCreateItemFromParsingName(Environment.GetFolderPath(defaultFolder), null, out IShellItem defaultFolderShellItem); + + // Handle shell item creation failure gracefully + if (hr.Failed) { - hr = PInvoke.SHCreateItemFromParsingName( - pszDefaultFolderPath, - null, - IID.IID_IShellItem, - (void**)pDefaultFolderShellItem.GetAddressOf()); - - // Handle shell item creation failure gracefully - if (hr.Failed) - { - App.Logger.LogWarning("Failed to create shell item for default folder '{0}'. HRESULT: 0x{1:X8}. Dialog will open without default folder.", Environment.GetFolderPath(defaultFolder), hr.Value); - // Continue without setting default folder rather than failing completely - } + App.Logger.LogWarning("Failed to create shell item for default folder '{0}'. HRESULT: 0x{1:X8}. Dialog will open without default folder.", Environment.GetFolderPath(defaultFolder), hr.Value); + // Continue without setting default folder rather than failing completely + } + else + { + pDefaultFolderShellItem = defaultFolderShellItem; } // Folder picker if (pickFoldersOnly) - pDialog.Get()->SetOptions(FILEOPENDIALOGOPTIONS.FOS_PICKFOLDERS); + pDialog.SetOptions(FILEOPENDIALOGOPTIONS.FOS_PICKFOLDERS); // Set the default folder to open in the dialog (only if creation succeeded) - if (pDefaultFolderShellItem.Get() is not null) + if (pDefaultFolderShellItem is not null) { - pDialog.Get()->SetFolder(pDefaultFolderShellItem.Get()); - pDialog.Get()->SetDefaultFolder(pDefaultFolderShellItem.Get()); + pDialog.SetFolder(pDefaultFolderShellItem); + pDialog.SetDefaultFolder(pDefaultFolderShellItem); } // Show the dialog - hr = pDialog.Get()->Show(new HWND(hWnd)); + hr = pDialog.Show(new HWND(hWnd)); if (hr.Value == unchecked((int)0x800704C7)) // HRESULT_FROM_WIN32(ERROR_CANCELLED) return false; @@ -93,12 +89,12 @@ public unsafe bool Open_FileOpenDialog(nint hWnd, bool pickFoldersOnly, string[] } // Get the file that user chose - using ComPtr pResultShellItem = default; - pDialog.Get()->GetResult(pResultShellItem.GetAddressOf()); - if (pResultShellItem.Get() is null) + pDialog.GetResult(out IShellItem pResultShellItem); + if (pResultShellItem is null) throw new COMException("FileOpenDialog returned invalid shell item."); - pResultShellItem.Get()->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out var lpFilePath); + pResultShellItem.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out var lpFilePath); filePath = lpFilePath.ToString(); + PInvoke.CoTaskMemFree(lpFilePath); return true; } @@ -121,11 +117,10 @@ public unsafe bool Open_FileSaveDialog(nint hWnd, bool pickFoldersOnly, string[] try { - using ComPtr pDialog = default; - HRESULT hr = pDialog.CoCreateInstance(CLSID.CLSID_FileSaveDialog, null, CLSCTX.CLSCTX_INPROC_SERVER); + HRESULT hr = PInvoke.CoCreateInstance(typeof(FileSaveDialog).GUID, null, CLSCTX.CLSCTX_INPROC_SERVER, out IFileSaveDialog? pDialog); // Handle COM creation failure gracefully - if (hr.Failed) + if (hr.Failed || pDialog is null) { App.Logger.LogError("Failed to create IFileSaveDialog COM object. HRESULT: 0x{0:X8}", hr.Value); return false; @@ -147,40 +142,37 @@ public unsafe bool Open_FileSaveDialog(nint hWnd, bool pickFoldersOnly, string[] } // Set the file type using the extension list - pDialog.Get()->SetFileTypes(extensions.ToArray()); + pDialog.SetFileTypes(extensions.ToArray()); } // Get the default shell folder (My Computer) - using ComPtr pDefaultFolderShellItem = default; - fixed (char* pszDefaultFolderPath = Environment.GetFolderPath(defaultFolder)) - { - hr = PInvoke.SHCreateItemFromParsingName( - pszDefaultFolderPath, - null, - IID.IID_IShellItem, - (void**)pDefaultFolderShellItem.GetAddressOf()); + IShellItem? pDefaultFolderShellItem = null; + hr = PInvoke.SHCreateItemFromParsingName(Environment.GetFolderPath(defaultFolder), null, out IShellItem defaultFolderShellItem); - // Handle shell item creation failure gracefully - if (hr.Failed) - { - App.Logger.LogWarning("Failed to create shell item for default folder '{0}'. HRESULT: 0x{1:X8}. Dialog will open without default folder.", Environment.GetFolderPath(defaultFolder), hr.Value); - // Continue without setting default folder rather than failing completely - } + // Handle shell item creation failure gracefully + if (hr.Failed) + { + App.Logger.LogWarning("Failed to create shell item for default folder '{0}'. HRESULT: 0x{1:X8}. Dialog will open without default folder.", Environment.GetFolderPath(defaultFolder), hr.Value); + // Continue without setting default folder rather than failing completely + } + else + { + pDefaultFolderShellItem = defaultFolderShellItem; } // Folder picker if (pickFoldersOnly) - pDialog.Get()->SetOptions(FILEOPENDIALOGOPTIONS.FOS_PICKFOLDERS); + pDialog.SetOptions(FILEOPENDIALOGOPTIONS.FOS_PICKFOLDERS); // Set the default folder to open in the dialog (only if creation succeeded) - if (pDefaultFolderShellItem.Get() is not null) + if (pDefaultFolderShellItem is not null) { - pDialog.Get()->SetFolder(pDefaultFolderShellItem.Get()); - pDialog.Get()->SetDefaultFolder(pDefaultFolderShellItem.Get()); + pDialog.SetFolder(pDefaultFolderShellItem); + pDialog.SetDefaultFolder(pDefaultFolderShellItem); } // Show the dialog - hr = pDialog.Get()->Show(new HWND(hWnd)); + hr = pDialog.Show(new HWND(hWnd)); if (hr.Value == unchecked((int)0x800704C7)) // HRESULT_FROM_WIN32(ERROR_CANCELLED) return false; @@ -192,12 +184,12 @@ public unsafe bool Open_FileSaveDialog(nint hWnd, bool pickFoldersOnly, string[] } // Get the file that user chose - using ComPtr pResultShellItem = default; - pDialog.Get()->GetResult(pResultShellItem.GetAddressOf()); - if (pResultShellItem.Get() is null) + pDialog.GetResult(out IShellItem pResultShellItem); + if (pResultShellItem is null) throw new COMException("FileSaveDialog returned invalid shell item."); - pResultShellItem.Get()->GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out var lpFilePath); + pResultShellItem.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out var lpFilePath); filePath = lpFilePath.ToString(); + PInvoke.CoTaskMemFree(lpFilePath); return true; } diff --git a/src/Files.App/Services/Windows/WindowsRecentItemsService.cs b/src/Files.App/Services/Windows/WindowsRecentItemsService.cs index 9024d8a0770c..7fd74ed04cc6 100644 --- a/src/Files.App/Services/Windows/WindowsRecentItemsService.cs +++ b/src/Files.App/Services/Windows/WindowsRecentItemsService.cs @@ -1,4 +1,4 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. using Microsoft.Extensions.Logging; @@ -121,10 +121,12 @@ public unsafe bool Remove(RecentItem item) { try { - using ComPtr pContextMenu = default; - HRESULT hr = item.ShellItem.Get()->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf()); + HRESULT hr = item.ShellItem.BindToHandler(null, PInvoke.BHID_SFUIObject, out IContextMenu? pContextMenu); + if (hr.Failed || pContextMenu is null) + return false; + HMENU hMenu = PInvoke.CreatePopupMenu(); - hr = pContextMenu.Get()->QueryContextMenu(hMenu, 0, 1, 0x7FFF, PInvoke.CMF_OPTIMIZEFORINVOKE); + hr = pContextMenu.QueryContextMenu(hMenu, 0, 1, 0x7FFF, PInvoke.CMF_OPTIMIZEFORINVOKE); // Initialize invocation info CMINVOKECOMMANDINFO cmi = default; @@ -138,13 +140,13 @@ public unsafe bool Remove(RecentItem item) { // Try unpin files cmi.lpVerb = new(pVerb1); - hr = pContextMenu.Get()->InvokeCommand(cmi); + hr = pContextMenu.InvokeCommand(cmi); if (hr == HRESULT.S_OK) return true; // Try unpin folders cmi.lpVerb = new(pVerb2); - hr = pContextMenu.Get()->InvokeCommand(cmi); + hr = pContextMenu.InvokeCommand(cmi); if (hr == HRESULT.S_OK) return true; @@ -153,7 +155,7 @@ public unsafe bool Remove(RecentItem item) // won't be removed via unpinfromhome verb. // Try unpin folders again cmi.lpVerb = new(pVerb3); - hr = pContextMenu.Get()->InvokeCommand(cmi); + hr = pContextMenu.InvokeCommand(cmi); if (hr == HRESULT.S_OK) return true; } @@ -195,37 +197,40 @@ private unsafe bool UpdateRecentItems(bool isFolder) : "Shell:::{679F85CB-0220-4080-B29B-5540CC05AAB6}"; // Quick Access folder (recent files) // Get IShellItem of the shell folder - using ComPtr pFolderShellItem = default; - fixed (char* pszFolderShellPath = szFolderShellPath) - hr = PInvoke.SHCreateItemFromParsingName(pszFolderShellPath, null, IID.IID_IShellItem, (void**)pFolderShellItem.GetAddressOf()); + hr = PInvoke.SHCreateItemFromParsingName(szFolderShellPath, null, out IShellItem pFolderShellItem); + if (hr.Failed) + return false; // Get IEnumShellItems of the quick access shell folder - using ComPtr pEnumShellItems = default; - hr = pFolderShellItem.Get()->BindToHandler(null, BHID.BHID_EnumItems, IID.IID_IEnumShellItems, (void**)pEnumShellItems.GetAddressOf()); + hr = pFolderShellItem.BindToHandler(null, PInvoke.BHID_EnumItems, out IEnumShellItems? pEnumShellItems); + if (hr.Failed || pEnumShellItems is null) + return false; // Enumerate recent items and populate the list int index = 0; List recentItems = []; - ComPtr pShellItem = default; // Do not dispose in this method to use later to prepare for its deletion - while (pEnumShellItems.Get()->Next(1, pShellItem.GetAddressOf()) == HRESULT.S_OK) + IShellItem[] pShellItems = new IShellItem[1]; + while (pEnumShellItems.Next(1, pShellItems, null) == HRESULT.S_OK) { + IShellItem shellItem = pShellItems[0]; + // Get top 20 items if (index is 20) break; // Exclude folders, but keep archives (ZIP/7z/RAR) which the shell reports as both folder and stream. - if (pShellItem.Get()->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER | SFGAO_FLAGS.SFGAO_STREAM, out var attribute).Succeeded && + if (shellItem.GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER | SFGAO_FLAGS.SFGAO_STREAM, out var attribute).Succeeded && (attribute & SFGAO_FLAGS.SFGAO_FOLDER) == SFGAO_FLAGS.SFGAO_FOLDER && (attribute & SFGAO_FLAGS.SFGAO_STREAM) == 0) continue; // Get the target path - pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEEDITING, out var szDisplayName); + shellItem.GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEEDITING, out var szDisplayName); var targetPath = szDisplayName.ToString(); PInvoke.CoTaskMemFree(szDisplayName.Value); // Get the display name - pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out szDisplayName); + shellItem.GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out szDisplayName); var fileName = szDisplayName.ToString(); PInvoke.CoTaskMemFree(szDisplayName.Value); @@ -235,18 +240,26 @@ private unsafe bool UpdateRecentItems(bool isFolder) fileName = string.IsNullOrEmpty(fileNameWithoutExtension) ? SystemIO.Path.GetFileName(fileName) : fileNameWithoutExtension; // Get the date last modified - using ComPtr pShellItem2 = default; - hr = pShellItem.Get()->QueryInterface(IID.IID_IShellItem2, (void**)pShellItem2.GetAddressOf()); - hr = PInvoke.PSGetPropertyKeyFromName("System.DateModified", out var propertyKey); - hr = pShellItem2.Get()->GetString(propertyKey, out var szPropertyValue); - if (DateTime.TryParse(szPropertyValue.ToString(), out var lastModified)) - lastModified = DateTime.MinValue; + DateTime lastModified = DateTime.MinValue; + + if (shellItem is IShellItem2 shellItem2 && + PInvoke.PSGetPropertyKeyFromName("System.DateModified", out var propertyKey).Succeeded) + { + hr = shellItem2.GetString(propertyKey, out var szPropertyValue); + if (hr.Succeeded) + { + if (!DateTime.TryParse(szPropertyValue.ToString(), out lastModified)) + lastModified = DateTime.MinValue; + + PInvoke.CoTaskMemFree(szPropertyValue); + } + } recentItems.Add(new() { Path = targetPath, Name = fileName, - ShellItem = pShellItem, + ShellItem = shellItem, LastModified = lastModified, }); diff --git a/src/Files.App/Services/Windows/WindowsWallpaperService.cs b/src/Files.App/Services/Windows/WindowsWallpaperService.cs index be2f18069759..bd5be966e96e 100644 --- a/src/Files.App/Services/Windows/WindowsWallpaperService.cs +++ b/src/Files.App/Services/Windows/WindowsWallpaperService.cs @@ -5,6 +5,7 @@ using Windows.System.UserProfile; using Windows.Win32; using Windows.Win32.Foundation; +using Windows.Win32.System.Com; using Windows.Win32.UI.Shell; using Windows.Win32.UI.Shell.Common; @@ -17,11 +18,12 @@ public sealed class WindowsWallpaperService : IWindowsWallpaperService public unsafe void SetDesktopWallpaper(string szPath) { // Instantiate IDesktopWallpaper - using ComPtr pDesktopWallpaper = default; - HRESULT hr = pDesktopWallpaper.CoCreateInstance(CLSID.CLSID_DesktopWallpaper); + HRESULT hr = PInvoke.CoCreateInstance(typeof(DesktopWallpaper).GUID, null, CLSCTX.CLSCTX_LOCAL_SERVER, out IDesktopWallpaper? pDesktopWallpaper); + if (hr.Failed || pDesktopWallpaper is null) + return; // Get total count of all available monitors - hr = pDesktopWallpaper.Get()->GetMonitorDevicePathCount(out var dwMonitorCount); + hr = pDesktopWallpaper.GetMonitorDevicePathCount(out var dwMonitorCount); fixed (char* pszPath = szPath) { @@ -31,8 +33,9 @@ public unsafe void SetDesktopWallpaper(string szPath) for (uint dwIndex = 0u; dwIndex < dwMonitorCount; dwIndex++) { // Set the wallpaper - hr = pDesktopWallpaper.Get()->GetMonitorDevicePathAt(dwIndex, &pMonitorId); - hr = pDesktopWallpaper.Get()->SetWallpaper(pMonitorId, pszPath); + hr = pDesktopWallpaper.GetMonitorDevicePathAt(dwIndex, out pMonitorId); + hr = pDesktopWallpaper.SetWallpaper(pMonitorId.ToString(), szPath); + PInvoke.CoTaskMemFree(pMonitorId); pMonitorId = default; } @@ -43,8 +46,9 @@ public unsafe void SetDesktopWallpaper(string szPath) public unsafe void SetDesktopSlideshow(string[] aszPaths) { // Instantiate IDesktopWallpaper - using ComPtr pDesktopWallpaper = default; - HRESULT hr = pDesktopWallpaper.CoCreateInstance(CLSID.CLSID_DesktopWallpaper); + HRESULT hr = PInvoke.CoCreateInstance(typeof(DesktopWallpaper).GUID, null, CLSCTX.CLSCTX_LOCAL_SERVER, out IDesktopWallpaper? pDesktopWallpaper); + if (hr.Failed || pDesktopWallpaper is null) + return; uint dwCount = (uint)aszPaths.Length; ITEMIDLIST** ppItemIdList = stackalloc ITEMIDLIST*[aszPaths.Length]; @@ -54,16 +58,15 @@ public unsafe void SetDesktopSlideshow(string[] aszPaths) ppItemIdList[dwIndex] = PInvoke.ILCreateFromPath(aszPaths[dwIndex]); // Get an IShellItemArray from the array of the PIDL - using ComPtr pShellItemArray = default; - hr = PInvoke.SHCreateShellItemArrayFromIDLists(dwCount, ppItemIdList, pShellItemArray.GetAddressOf()); + hr = PInvoke.SHCreateShellItemArrayFromIDLists(dwCount, ppItemIdList, out IShellItemArray pShellItemArray); // Release the allocated PIDL for (uint dwIndex = 0u; dwIndex < dwCount; dwIndex++) PInvoke.CoTaskMemFree((void*)ppItemIdList[dwIndex]); // Set the slideshow and its position - hr = pDesktopWallpaper.Get()->SetSlideshow(pShellItemArray.Get()); - hr = pDesktopWallpaper.Get()->SetPosition(DESKTOP_WALLPAPER_POSITION.DWPOS_FILL); + hr = pDesktopWallpaper.SetSlideshow(pShellItemArray); + hr = pDesktopWallpaper.SetPosition(DESKTOP_WALLPAPER_POSITION.DWPOS_FILL); } /// diff --git a/src/Files.App/Utils/Shell/DetectionAndSharingHelper.cs b/src/Files.App/Utils/Shell/DetectionAndSharingHelper.cs index c58e6f34d633..5c99a315946f 100644 --- a/src/Files.App/Utils/Shell/DetectionAndSharingHelper.cs +++ b/src/Files.App/Utils/Shell/DetectionAndSharingHelper.cs @@ -13,13 +13,13 @@ internal static class DetectionAndSharingHelper { public static unsafe NetworkAvailability GetNetworkAvailability() { - using ComPtr dtsh = CreateDetectionAndSharing(); + IDetectionAndSharing dtsh = CreateDetectionAndSharing(); var availability = NetworkAvailability.None; - if (IsEnabled(dtsh.Get(), DTSH_TYPE.DTSH_NETWORK_DISCOVERY)) + if (IsEnabled(dtsh, DTSH_TYPE.DTSH_NETWORK_DISCOVERY)) availability |= NetworkAvailability.Discovery; - if (IsEnabled(dtsh.Get(), DTSH_TYPE.DTSH_FILE_SHARING)) + if (IsEnabled(dtsh, DTSH_TYPE.DTSH_FILE_SHARING)) availability |= NetworkAvailability.Sharing; return availability; @@ -27,32 +27,28 @@ public static unsafe NetworkAvailability GetNetworkAvailability() public static unsafe void OpenNetworkSharingSettings() { - using ComPtr controlPanel = default; - HRESULT hr = controlPanel.CoCreateInstance(CLSID.CLSID_OpenControlPanel, null, CLSCTX.CLSCTX_INPROC_SERVER); + HRESULT hr = PInvoke.CoCreateInstance(CLSID.CLSID_OpenControlPanel, null, CLSCTX.CLSCTX_INPROC_SERVER, out IOpenControlPanel? controlPanel); ThrowIfFailed(hr, "Failed to create open control panel object."); fixed (char* name = "Microsoft.NetworkAndSharingCenter") fixed (char* page = "Advanced") { - hr = controlPanel.Get()->Open(name, page, null); + hr = controlPanel!.Open(name, page, null); ThrowIfFailed(hr, "Failed to open advanced sharing settings."); } } - private static unsafe ComPtr CreateDetectionAndSharing() + private static unsafe IDetectionAndSharing CreateDetectionAndSharing() { - ComPtr dtsh = default; - HRESULT hr = dtsh.CoCreateInstance(CLSID.CLSID_DetectionAndSharing, null, CLSCTX.CLSCTX_INPROC_SERVER); + HRESULT hr = PInvoke.CoCreateInstance(CLSID.CLSID_DetectionAndSharing, null, CLSCTX.CLSCTX_INPROC_SERVER, out IDetectionAndSharing? dtsh); ThrowIfFailed(hr, "Failed to create detection and sharing object."); - return dtsh; + return dtsh!; } - private static unsafe bool IsEnabled(IDetectionAndSharing* dtsh, DTSH_TYPE type) + private static unsafe bool IsEnabled(IDetectionAndSharing dtsh, DTSH_TYPE type) { - DTSH_STATE state; - DTSH_ACTION action; - HRESULT hr = dtsh->GetStatus(type, &state, &action); + HRESULT hr = dtsh.GetStatus(type, out DTSH_STATE state, out _); ThrowIfFailed(hr, $"Failed to get {type} status."); return state is DTSH_STATE.DTSH_ON; diff --git a/src/Files.App/Utils/Shell/ItemStreamHelper.cs b/src/Files.App/Utils/Shell/ItemStreamHelper.cs index 61b3c816b579..065b892bd0c1 100644 --- a/src/Files.App/Utils/Shell/ItemStreamHelper.cs +++ b/src/Files.App/Utils/Shell/ItemStreamHelper.cs @@ -1,46 +1,31 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. -using System.Runtime.InteropServices; using Windows.Win32; using Windows.Win32.System.Com; +using Windows.Win32.UI.Shell; namespace Files.App.Utils.Shell { public static class ItemStreamHelper { - static readonly Guid IShellItemIid = Guid.ParseExact("43826d1e-e718-42ee-bc55-a1e261c37bfe", "d"); - - public static unsafe IntPtr IShellItemFromPath(string path) + public static IShellItem? IShellItemFromPath(string path) { - void* psi; - Guid iid = IShellItemIid; - var hr = PInvoke.SHCreateItemFromParsingName(path, null, ref iid, out psi); - if ((int)hr < 0) - return IntPtr.Zero; - return (IntPtr)psi; + var hr = PInvoke.SHCreateItemFromParsingName(path, null!, out IShellItem psi); + return hr.Failed ? null : psi; } - public static unsafe IntPtr IStreamFromPath(string path) + public static IStream? IStreamFromPath(string path) { - IStream* pstm; var hr = PInvoke.SHCreateStreamOnFileEx( path, (uint)(STGM.STGM_READ | STGM.STGM_FAILIFTHERE | STGM.STGM_SHARE_DENY_NONE), 0, false, null, - &pstm); - - if ((int)hr < 0) - return IntPtr.Zero; + out IStream pstm); - return (IntPtr)pstm; - } - - public static void ReleaseObject(IntPtr obj) - { - Marshal.Release(obj); + return hr.Failed ? null : pstm; } } } diff --git a/src/Files.App/Utils/Shell/LaunchHelper.cs b/src/Files.App/Utils/Shell/LaunchHelper.cs index 1ca4da412470..3c59b9b0e776 100644 --- a/src/Files.App/Utils/Shell/LaunchHelper.cs +++ b/src/Files.App/Utils/Shell/LaunchHelper.cs @@ -1,4 +1,4 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. using Files.Shared.Helpers; @@ -7,6 +7,7 @@ using Vanara.PInvoke; using Vanara.Windows.Shell; using Windows.Win32; +using Windows.Win32.System.Com; using Windows.Win32.UI.Shell; namespace Files.App.Utils.Shell @@ -18,10 +19,9 @@ public static class LaunchHelper { public unsafe static void LaunchSettings(string page) { - using ComPtr pApplicationActivationManager = default; - pApplicationActivationManager.CoCreateInstance(CLSID.CLSID_ApplicationActivationManager); + PInvoke.CoCreateInstance(typeof(ApplicationActivationManager).GUID, null, CLSCTX.CLSCTX_LOCAL_SERVER, out IApplicationActivationManager? pApplicationActivationManager); - pApplicationActivationManager.Get()->ActivateApplication( + pApplicationActivationManager!.ActivateApplication( "windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel", page, ACTIVATEOPTIONS.AO_NONE, diff --git a/src/Files.App/Utils/Shell/OpenWithMenu.cs b/src/Files.App/Utils/Shell/OpenWithMenu.cs index 0cd4b34db816..c4b5350c5427 100644 --- a/src/Files.App/Utils/Shell/OpenWithMenu.cs +++ b/src/Files.App/Utils/Shell/OpenWithMenu.cs @@ -19,11 +19,11 @@ internal sealed class OpenWithMenu : IDisposable private static readonly ImageConverter IconConverter = new(); private readonly ThreadWithMessageQueue owningThread; - private unsafe IContextMenu* contextMenu; + private IContextMenu? contextMenu; private HMENU menu; private bool disposedValue; - private unsafe OpenWithMenu(IContextMenu* contextMenu, HMENU menu, ThreadWithMessageQueue owningThread) + private OpenWithMenu(IContextMenu contextMenu, HMENU menu, ThreadWithMessageQueue owningThread) { this.contextMenu = contextMenu; this.menu = menu; @@ -51,21 +51,15 @@ private unsafe OpenWithMenu(IContextMenu* contextMenu, HMENU menu, ThreadWithMes public async Task InvokeItem(int itemId) { - unsafe - { - if (itemId < 0 || contextMenu is null) - return false; - } + if (itemId < 0 || contextMenu is null) + return false; try { var currentWindows = Win32Helper.GetDesktopWindows(); var hr = await owningThread.PostMethod(() => { - unsafe - { - return InvokeItemCore(itemId); - } + return InvokeItemCore(itemId); }); if (hr.Failed) return false; @@ -94,50 +88,38 @@ private unsafe HRESULT InvokeItemCore(int itemId) nShow = (int)SHOW_WINDOW_CMD.SW_SHOWNORMAL, }; - return contextMenu->InvokeCommand(&commandInfo); + return contextMenu.InvokeCommand(commandInfo); } private static unsafe OpenWithMenu? Create(string path, ThreadWithMessageQueue owningThread) { - IContextMenu* openWithContextMenu = default; + IContextMenu? openWithContextMenu = null; HMENU hMenu = default; try { - using ComPtr openWithContextMenu2 = default; - using ComPtr shellExtInit = default; - using ComPtr shellItem = default; - using ComPtr dataObject = default; - - HRESULT hr = PInvoke.CoCreateInstance(CLSID.CLSID_OpenWithMenu, null, CLSCTX.CLSCTX_INPROC_SERVER, IID.IID_IContextMenu, (void**)&openWithContextMenu); - if (hr.ThrowIfFailedOnDebug().Failed) + HRESULT hr = PInvoke.CoCreateInstance(CLSID.CLSID_OpenWithMenu, null, CLSCTX.CLSCTX_INPROC_SERVER, out openWithContextMenu); + if (hr.ThrowIfFailedOnDebug().Failed || openWithContextMenu is null) return null; - hr = openWithContextMenu->QueryInterface(IID.IID_IContextMenu2, (void**)openWithContextMenu2.GetAddressOf()); - if (hr.ThrowIfFailedOnDebug().Failed) + if (openWithContextMenu is not IContextMenu2 openWithContextMenu2 || + openWithContextMenu is not IShellExtInit shellExtInit) return null; - hr = openWithContextMenu->QueryInterface(IID.IID_IShellExtInit, (void**)shellExtInit.GetAddressOf()); + hr = PInvoke.SHCreateItemFromParsingName(path, null, out IShellItem shellItem); if (hr.ThrowIfFailedOnDebug().Failed) return null; - fixed (char* pathPtr = path) - { - hr = PInvoke.SHCreateItemFromParsingName(pathPtr, null, IID.IID_IShellItem, (void**)shellItem.GetAddressOf()); - } - if (hr.ThrowIfFailedOnDebug().Failed) + hr = shellItem.BindToHandler(null, PInvoke.BHID_DataObject, out IDataObject? dataObject); + if (hr.ThrowIfFailedOnDebug().Failed || dataObject is null) return null; - hr = shellItem.Get()->BindToHandler(null, BHID.BHID_DataObject, IID.IID_IDataObject, (void**)dataObject.GetAddressOf()); - if (hr.ThrowIfFailedOnDebug().Failed) - return null; - - hr = shellExtInit.Get()->Initialize(null, dataObject.Get(), default); + hr = shellExtInit.Initialize(null, dataObject, null); if (hr.ThrowIfFailedOnDebug().Failed) return null; hMenu = PInvoke.CreatePopupMenu(); - hr = openWithContextMenu->QueryContextMenu(hMenu, 0, 1, 256, 0); + hr = openWithContextMenu.QueryContextMenu(hMenu, 0, 1, 256, 0); if (hr.ThrowIfFailedOnDebug().Failed) return null; @@ -145,12 +127,12 @@ private unsafe HRESULT InvokeItemCore(int itemId) if (hSubMenu.IsNull) return null; - hr = openWithContextMenu2.Get()->HandleMenuMsg(PInvoke.WM_INITMENUPOPUP, (WPARAM)(nuint)hSubMenu.Value, 0); + hr = openWithContextMenu2.HandleMenuMsg(PInvoke.WM_INITMENUPOPUP, (WPARAM)(nuint)hSubMenu.Value, 0); if (hr.ThrowIfFailedOnDebug().Failed) return null; var openWithMenu = new OpenWithMenu(openWithContextMenu, hMenu, owningThread); - openWithContextMenu = default; + openWithContextMenu = null; hMenu = default; openWithMenu.EnumMenuItems(hSubMenu); @@ -165,9 +147,6 @@ private unsafe HRESULT InvokeItemCore(int itemId) { if (!hMenu.IsNull) PInvoke.DestroyMenu(hMenu); - - if (openWithContextMenu is not null) - openWithContextMenu->Release(); } } @@ -277,14 +256,7 @@ public void Dispose() menu = default; } - unsafe - { - if (contextMenu is not null) - { - contextMenu->Release(); - contextMenu = null; - } - } + contextMenu = null; owningThread.Dispose(); disposedValue = true; diff --git a/src/Files.App/Utils/Shell/PreviewHandler.cs b/src/Files.App/Utils/Shell/PreviewHandler.cs index f153dd76ed49..12d70b0ab1fd 100644 --- a/src/Files.App/Utils/Shell/PreviewHandler.cs +++ b/src/Files.App/Utils/Shell/PreviewHandler.cs @@ -1,9 +1,14 @@ -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; using System.Runtime.InteropServices.Marshalling; using Windows.UI; using Windows.Win32; using Windows.Win32.Foundation; +using Windows.Win32.Graphics.Gdi; using Windows.Win32.System.Com; +using Windows.Win32.System.Ole; +using Windows.Win32.UI.Shell; +using Windows.Win32.UI.Shell.PropertiesSystem; +using Windows.Win32.UI.WindowsAndMessaging; namespace Files.App.Utils.Shell { @@ -12,129 +17,43 @@ namespace Files.App.Utils.Shell /// public sealed partial class PreviewHandler : IDisposable { - const int S_OK = 0; - const int S_FALSE = 1; - const int E_FAIL = unchecked((int)0x80004005); - const int E_SERVER_EXEC_FAILURE = unchecked((int)0x80080005); - - [StructLayout(LayoutKind.Sequential)] - public struct RECT(int left, int top, int right, int bottom) - { - public int Left = left; - public int Top = top; - public int Right = right; - public int Bottom = bottom; - } - - #region IPreviewHandlerFrame support - - [StructLayout(LayoutKind.Sequential)] - public struct PreviewHandlerFrameInfo - { - public IntPtr AcceleratorTableHandle; - public uint AcceleratorEntryCount; - } - - [GeneratedComInterface, Guid("fec87aaf-35f9-447a-adb7-20234491401a"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public partial interface IPreviewHandlerFrame - { - [PreserveSig] - int GetWindowContext(out PreviewHandlerFrameInfo pinfo); - [PreserveSig] - int TranslateAccelerator(nint pmsg); - } - [GeneratedComClass] - public sealed partial class PreviewHandlerFrame : IPreviewHandlerFrame, IDisposable + public sealed partial class PreviewHandlerFrame : IPreviewHandlerFrame { - bool disposed; nint hwnd; public PreviewHandlerFrame(nint frame) { - disposed = true; - disposed = false; hwnd = frame; } - public void Dispose() + [return: MarshalAs(UnmanagedType.Error)] + public unsafe HRESULT GetWindowContext(PREVIEWHANDLERFRAMEINFO* pinfo) { - disposed = true; + pinfo->haccel = HACCEL.Null; + pinfo->cAccelEntries = 0; + return HRESULT.S_OK; } - public int GetWindowContext(out PreviewHandlerFrameInfo pinfo) + [return: MarshalAs(UnmanagedType.Error)] + public unsafe HRESULT TranslateAccelerator(MSG* pmsg) { - pinfo.AcceleratorTableHandle = IntPtr.Zero; - pinfo.AcceleratorEntryCount = 0; - if (disposed) - return E_FAIL; - return S_OK; + return HRESULT.S_FALSE; } - - public int TranslateAccelerator(nint pmsg) - { - if (disposed) - return E_FAIL; - return S_FALSE; - } - } - - #endregion IPreviewHandlerFrame support - - #region IPreviewHandler major interfaces - - [GeneratedComInterface, Guid("8895b1c6-b41f-4c1c-a562-0d564250836f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal partial interface IPreviewHandler - { - [PreserveSig] - int SetWindow(nint hwnd, in RECT prc); - [PreserveSig] - int SetRect(in RECT prc); - [PreserveSig] - int DoPreview(); - [PreserveSig] - int Unload(); - [PreserveSig] - int SetFocus(); - [PreserveSig] - int QueryFocus(out nint phwnd); - // TranslateAccelerator is not used here. - } - - [GeneratedComInterface, Guid("196bf9a5-b346-4ef0-aa1e-5dcdb76768b1"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal partial interface IPreviewHandlerVisuals - { - [PreserveSig] - int SetBackgroundColor(uint color); - [PreserveSig] - int SetFont(nint plf); - [PreserveSig] - int SetTextColor(uint color); - } - - static uint ColorRefFromColor(Color color) - { - return (((uint)color.B) << 16) | (((uint)color.G) << 8) | ((uint)color.R); } - [GeneratedComInterface, Guid("fc4801a3-2ba9-11cf-a229-00aa003d7352"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal partial interface IObjectWithSite + static COLORREF ColorRefFromColor(Color color) { - [PreserveSig] - int SetSite(nint pUnkSite); - // GetSite is not used. + return new COLORREF((((uint)color.B) << 16) | (((uint)color.G) << 8) | color.R); } - #endregion IPreviewHandler major interfaces - bool disposed; bool init; bool shown; PreviewHandlerFrame? comSite; - nint hwnd; + HWND hwnd; IPreviewHandler? previewHandler; IPreviewHandlerVisuals? visuals; - readonly ComWrappers comWrappers = new StrategyBasedComWrappers(); public PreviewHandler(Guid clsid, nint frame) { @@ -142,7 +61,7 @@ public PreviewHandler(Guid clsid, nint frame) init = false; shown = false; comSite = new PreviewHandlerFrame(frame); - hwnd = frame; + hwnd = (HWND)frame; try { SetupHandler(clsid); @@ -151,110 +70,40 @@ public PreviewHandler(Guid clsid, nint frame) catch { previewHandler = null; - comSite.Dispose(); comSite = null; throw; } } - static readonly Guid IPreviewHandlerIid = Guid.ParseExact("8895b1c6-b41f-4c1c-a562-0d564250836f", "d"); - - unsafe void SetupHandler(Guid clsid) + void SetupHandler(Guid clsid) { - void* pphRaw; - var iid = IPreviewHandlerIid; var cannotCreate = "Cannot create class " + clsid.ToString() + " as IPreviewHandler."; var cannotCast = "Cannot cast class " + clsid.ToString() + " as IObjectWithSite."; var cannotSetSite = "Cannot set site to the preview handler object."; - // Important: manully calling CoCreateInstance is necessary. - // If we use Activator.CreateInstance(Type.GetTypeFromCLSID(...)), - // CLR will allow in-process server, which defeats isolation and - // creates strange bugs. - HRESULT hr = PInvoke.CoCreateInstance(&clsid, null, CLSCTX.CLSCTX_LOCAL_SERVER, &iid, &pphRaw); - IntPtr pph = (IntPtr)pphRaw; + + HRESULT hr = PInvoke.CoCreateInstance(clsid, null, CLSCTX.CLSCTX_LOCAL_SERVER, out previewHandler); + // See https://blogs.msdn.microsoft.com/adioltean/2005/06/24/when-cocreateinstance-returns-0x80080005-co_e_server_exec_failure/ // CO_E_SERVER_EXEC_FAILURE also tends to happen when debugging in Visual Studio. // Moreover, to create the instance in a server at low integrity level, we need // to use another thread with low mandatory label. We keep it simple by creating // a same-integrity object. - if (hr.Value == E_SERVER_EXEC_FAILURE) - { - hr = PInvoke.CoCreateInstance(&clsid, null, CLSCTX.CLSCTX_LOCAL_SERVER, &iid, &pphRaw); - pph = (IntPtr)pphRaw; - } + //if (hr.Value == E_SERVER_EXEC_FAILURE) + // hr = PInvoke.CoCreateInstance(clsid, null, CLSCTX.CLSCTX_LOCAL_SERVER, out previewHandlerObject); + if (hr.Failed) throw new COMException(cannotCreate, hr.Value); - var previewHandlerObject = comWrappers.GetOrCreateObjectForComInstance(pph, CreateObjectFlags.UniqueInstance); - previewHandler = previewHandlerObject as IPreviewHandler; - - if (previewHandler == null) - { - throw new COMException(cannotCreate); - } - var objectWithSite = previewHandlerObject as IObjectWithSite; - if (objectWithSite == null) - throw new COMException(cannotCast); - int setSiteHr = objectWithSite.SetSite(comWrappers.GetOrCreateComInterfaceForObject(comSite, CreateComInterfaceFlags.None)); - if (setSiteHr < 0) - throw new COMException(cannotSetSite, setSiteHr); - visuals = previewHandlerObject as IPreviewHandlerVisuals; - } - - #region Initialization interfaces - [GeneratedComInterface, Guid("0000000c-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - public partial interface IStream - { - // ISequentialStream portion - void Read([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1), Out] byte[] pv, int cb, nint pcbRead); - void Write([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, int cb, nint pcbWritten); - - // IStream portion - void Seek(long dlibMove, int dwOrigin, nint plibNewPosition); - void SetSize(long libNewSize); - void CopyTo(IStream pstm, long cb, nint pcbRead, nint pcbWritten); - void Commit(int grfCommitFlags); - void Revert(); - void LockRegion(long libOffset, long cb, int dwLockType); - void UnlockRegion(long libOffset, long cb, int dwLockType); - } - - [GeneratedComInterface, Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal partial interface IInitializeWithStream - { - [PreserveSig] - int Initialize(IStream psi, STGM grfMode); - } - - [GeneratedComInterface, Guid("b824b49d-22ac-4161-ac8a-9916e8fa3f7f"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal partial interface IInitializeWithStreamNative - { - [PreserveSig] - int Initialize(IntPtr psi, STGM grfMode); - } - - static readonly Guid IInitializeWithStreamIid = Guid.ParseExact("b824b49d-22ac-4161-ac8a-9916e8fa3f7f", "d"); - - [GeneratedComInterface, Guid("b7d14566-0509-4cce-a71f-0a554233bd9b"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal partial interface IInitializeWithFile - { - [PreserveSig] - int Initialize([MarshalAs(UnmanagedType.LPWStr)] string pszFilePath, STGM grfMode); - } + if (previewHandler is not IObjectWithSite objectWithSite) + throw new COMException(cannotCast, HRESULT.E_NOINTERFACE.Value); - static readonly Guid IInitializeWithFileIid = Guid.ParseExact("b7d14566-0509-4cce-a71f-0a554233bd9b", "d"); + hr = objectWithSite.SetSite(comSite!); + if (hr.Failed) + throw new COMException(cannotSetSite, hr.Value); - [GeneratedComInterface, Guid("7f73be3f-fb79-493c-a6c7-7ee14e245841"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal partial interface IInitializeWithItem - { - [PreserveSig] - int Initialize(IntPtr psi, STGM grfMode); + visuals = previewHandler as IPreviewHandlerVisuals; } - static readonly Guid IInitializeWithItemIid = Guid.ParseExact("7f73be3f-fb79-493c-a6c7-7ee14e245841", "d"); - - #endregion - /// /// Tries to initialize the preview handler with an IStream. /// @@ -267,41 +116,18 @@ public bool InitWithStream(IStream stream, STGM mode) { if (mode != STGM.STGM_READ && mode != STGM.STGM_READWRITE) throw new ArgumentOutOfRangeException("mode", mode, "The argument mode must be Read or ReadWrite."); - var iws = previewHandler as IInitializeWithStream; - if (iws == null) - return false; - var hr = iws.Initialize(stream, mode); - if (hr == (int)HRESULT.E_NOTIMPL) - return false; - if (hr < 0) - throw new COMException("IInitializeWithStream.Initialize failed.", hr); - init = true; - return true; - } - /// - /// Same as InitWithStream(IStream, STGM). - /// - /// See InitWithStream(IStream, STGM). - /// See InitWithStream(IStream, STGM). - /// The native pointer to the IStream interface. - /// The storage mode. - /// True or false, see InitWithStream(IStream, STGM). - public bool InitWithStream(IntPtr pStream, STGM mode) - { - EnsureNotDisposed(); - EnsureNotInitialized(); - if (mode != STGM.STGM_READ && mode != STGM.STGM_READWRITE) - throw new ArgumentOutOfRangeException("mode", mode, "The argument mode must be Read or ReadWrite."); - var iws = previewHandler as IInitializeWithStreamNative; - if (iws == null) + if (previewHandler is not IInitializeWithStream initializeWithStream) return false; - var hr = iws.Initialize(pStream, mode); - if (hr == (int)HRESULT.E_NOTIMPL) + + HRESULT hr = initializeWithStream.Initialize(stream, (uint)mode); + if (hr == HRESULT.E_NOTIMPL) return false; - if (hr < 0) - throw new COMException("IInitializeWithStream.Initialize failed.", hr); + if (hr.Failed) + throw new COMException("IInitializeWithStream.Initialize failed.", hr.Value); + init = true; + return true; } @@ -310,24 +136,28 @@ public bool InitWithStream(IntPtr pStream, STGM mode) /// /// See InitWithStream(IStream, STGM). /// See InitWithStream(IStream, STGM). - /// The native pointer to the IShellItem interface. + /// The IShellItem interface used to initialize the preview handler. /// The storage mode. /// True or false, see InitWithStream(IStream, STGM). - public bool InitWithItem(IntPtr psi, STGM mode) + public bool InitWithItem(IShellItem psi, STGM mode) { EnsureNotDisposed(); EnsureNotInitialized(); + if (mode != STGM.STGM_READ && mode != STGM.STGM_READWRITE) throw new ArgumentOutOfRangeException("mode", mode, "The argument mode must be Read or ReadWrite."); - var iwi = previewHandler as IInitializeWithItem; - if (iwi == null) + + if (previewHandler is not IInitializeWithItem initializeWithItem) return false; - var hr = iwi.Initialize(psi, mode); - if (hr == (int)HRESULT.E_NOTIMPL) + + HRESULT hr = initializeWithItem.Initialize(psi, (uint)mode); + if (hr == HRESULT.E_NOTIMPL) return false; - if (hr < 0) - throw new COMException("IInitializeWithItem.Initialize failed.", hr); + if (hr.Failed) + throw new COMException("IInitializeWithItem.Initialize failed.", hr.Value); + init = true; + return true; } @@ -343,17 +173,21 @@ public bool InitWithFile(string path, STGM mode) { EnsureNotDisposed(); EnsureNotInitialized(); + if (mode != STGM.STGM_READ && mode != STGM.STGM_READWRITE) throw new ArgumentOutOfRangeException("mode", mode, "The argument mode must be Read or ReadWrite."); - var iwf = previewHandler as IInitializeWithFile; - if (iwf == null) + + if (previewHandler is not IInitializeWithFile initializeWithFile) return false; - var hr = iwf.Initialize(path, mode); - if (hr == (int)HRESULT.E_NOTIMPL) + + HRESULT hr = initializeWithFile.Initialize(path, (uint)mode); + if (hr == HRESULT.E_NOTIMPL) return false; - if (hr < 0) - throw new COMException("IInitializeWithFile.Initialize failed.", hr); + if (hr.Failed) + throw new COMException("IInitializeWithFile.Initialize failed.", hr.Value); + init = true; + return true; } @@ -365,7 +199,6 @@ public bool InitWithFile(string path, STGM mode) public bool InitWithFileWithEveryWay(string path) { var exceptions = new List(); - var pobj = IntPtr.Zero; // Why should we try IStream first? // Because that gives us the best security. // If we initialize with string or IShellItem, @@ -374,21 +207,15 @@ public bool InitWithFileWithEveryWay(string path) // file for read/write exclusively. try { - pobj = ItemStreamHelper.IStreamFromPath(path); - if (pobj != IntPtr.Zero - && InitWithStream(pobj, STGM.STGM_READ)) + var stream = ItemStreamHelper.IStreamFromPath(path); + if (stream is not null && InitWithStream(stream, STGM.STGM_READ)) return true; } catch (Exception ex) { exceptions.Add(ex); } - finally - { - if (pobj != IntPtr.Zero) - ItemStreamHelper.ReleaseObject(pobj); - pobj = IntPtr.Zero; - } + // Next try file because that could save us some P/Invokes. try { @@ -401,9 +228,8 @@ public bool InitWithFileWithEveryWay(string path) } try { - pobj = ItemStreamHelper.IShellItemFromPath(path); - if (pobj != IntPtr.Zero - && InitWithItem(pobj, STGM.STGM_READ)) + var shellItem = ItemStreamHelper.IShellItemFromPath(path); + if (shellItem is not null && InitWithItem(shellItem, STGM.STGM_READ)) return true; if (exceptions.Count == 0) throw new NotSupportedException("The object cannot be initialized at all."); @@ -412,12 +238,7 @@ public bool InitWithFileWithEveryWay(string path) { exceptions.Add(ex); } - finally - { - if (pobj != IntPtr.Zero) - ItemStreamHelper.ReleaseObject(pobj); - pobj = IntPtr.Zero; - } + throw new AggregateException(exceptions); } @@ -430,7 +251,7 @@ public bool ResetWindow() //EnsureInitialized(); if (!init) return false; - var hr = previewHandler.SetWindow(hwnd, new()); + var hr = previewHandler.SetWindow(hwnd, new RECT()); return hr >= 0; } @@ -474,9 +295,9 @@ public bool SetForeground(Color color) /// /// The LogFontW reference. /// Whether the call succeeds. - public bool SetFont(nint font) + public unsafe bool SetFont(nint font) { - var hr = visuals?.SetFont(font); + var hr = visuals?.SetFont(*(LOGFONTW*)font); return hr.HasValue && hr.Value >= 0; } @@ -519,11 +340,12 @@ public nint QueryFocus() if (!init) return IntPtr.Zero; EnsureShown(); - nint result; - var hr = previewHandler.QueryFocus(out result); + + var hr = previewHandler.QueryFocus(out var result); if (hr < 0) return IntPtr.Zero; - return result; + + return (nint)result; } void EnsureNotDisposed() @@ -571,7 +393,7 @@ public void Dispose() init = false; previewHandler?.Unload(); - comSite?.Dispose(); + comSite = null; GC.SuppressFinalize(this); } diff --git a/src/Files.App/Utils/Storage/Helpers/MtpHelpers.cs b/src/Files.App/Utils/Storage/Helpers/MtpHelpers.cs index 955b58797f79..7bb901b506ab 100644 --- a/src/Files.App/Utils/Storage/Helpers/MtpHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/MtpHelpers.cs @@ -17,14 +17,19 @@ public static class MtpHelpers /// Resolves a \\?\DeviceName\path to a shell Portable Devices namespace path /// so that shell APIs like work correctly. /// - public unsafe static string? ResolveMtpShellPath(string mtpPath) + public static string? ResolveMtpShellPath(string mtpPath) { var withoutPrefix = mtpPath.AsSpan(4); var sep = withoutPrefix.IndexOf('\\'); var deviceName = (sep >= 0 ? withoutPrefix[..sep] : withoutPrefix).ToString(); if (!_deviceParsingNames.TryGetValue(deviceName, out var parsingName)) - _deviceParsingNames[deviceName] = parsingName = FindDeviceParsingName(deviceName); + { + unsafe + { + _deviceParsingNames[deviceName] = parsingName = FindDeviceParsingName(deviceName); + } + } return parsingName is null ? null : sep >= 0 ? Path.Combine(parsingName, withoutPrefix[(sep + 1)..].ToString()) @@ -33,33 +38,30 @@ public static class MtpHelpers private unsafe static string? FindDeviceParsingName(string deviceName) { - using ComPtr pComputer = default; - Guid folderId = new("0AC0837C-BBF8-452A-850D-79D08E667CA7"); - PInvoke.SHGetKnownFolderItem(&folderId, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, IID.IID_IShellItem, (void**)pComputer.GetAddressOf()); - - if (pComputer.IsNull) + HRESULT hr = PInvoke.SHGetKnownFolderItem(FOLDERID.FOLDERID_ComputerFolder, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, null, out IShellItem computerFolderItem); + if (hr.ThrowIfFailedOnDebug().Failed) return null; - using ComPtr pEnum = default; - pComputer.Get()->BindToHandler(null, BHID.BHID_EnumItems, IID.IID_IEnumShellItems, (void**)pEnum.GetAddressOf()); - - if (pEnum.IsNull) + hr = computerFolderItem.BindToHandler(null, PInvoke.BHID_EnumItems, out IEnumShellItems? pEnum); + if (hr.ThrowIfFailedOnDebug().Failed || pEnum is null) return null; + IShellItem[] children = new IShellItem[1]; while (true) { - using ComPtr pChild = default; - if (pEnum.Get()->Next(1, pChild.GetAddressOf()) != HRESULT.S_OK) + if (pEnum.Next(children) != HRESULT.S_OK) break; - pChild.Get()->GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out var szName); + IShellItem pChild = children[0]; + + pChild.GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out var szName); var name = szName.ToString(); PInvoke.CoTaskMemFree(szName.Value); if (!deviceName.StartsWith(name, StringComparison.OrdinalIgnoreCase)) continue; - pChild.Get()->GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, out var szParsing); + pChild.GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEPARSING, out var szParsing); var result = szParsing.ToString(); PInvoke.CoTaskMemFree(szParsing.Value); return result; diff --git a/src/Files.App/Utils/Storage/Helpers/SyncRootHelpers.cs b/src/Files.App/Utils/Storage/Helpers/SyncRootHelpers.cs index 042fb8564e1a..3f58d9be282e 100644 --- a/src/Files.App/Utils/Storage/Helpers/SyncRootHelpers.cs +++ b/src/Files.App/Utils/Storage/Helpers/SyncRootHelpers.cs @@ -1,17 +1,16 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. using Microsoft.Win32; using Windows.Win32; using Windows.Win32.System.Com; using Windows.Win32.System.WinRT; -using WinRT; namespace Files.App.Utils.Storage { internal static class SyncRootHelpers { - private static unsafe (bool Success, ulong Capacity, ulong Used) GetSyncRootQuotaFromSyncRootId(string syncRootId) + private static (bool Success, ulong Capacity, ulong Used) GetSyncRootQuotaFromSyncRootId(string syncRootId) { using var key = Registry.LocalMachine.OpenSubKey($"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SyncRootManager\\{syncRootId}"); if (key?.GetValue("StorageProviderStatusUISourceFactory") is not string factoryClsidString || @@ -19,29 +18,17 @@ private static unsafe (bool Success, ulong Capacity, ulong Used) GetSyncRootQuot return (false, 0, 0); ulong ulTotalSize = 0ul, ulUsedSize = 0ul; - using ComPtr pStorageProviderStatusUISourceFactory = default; - using ComPtr pStorageProviderStatusUISource = default; - using ComPtr pStorageProviderStatusUI = default; - using ComPtr pStorageProviderQuotaUI = default; - if (PInvoke.CoCreateInstance( - &factoryClsid, - null, - CLSCTX.CLSCTX_LOCAL_SERVER, - IID.IID_IStorageProviderStatusUISourceFactory, - (void**)pStorageProviderStatusUISourceFactory.GetAddressOf()).ThrowIfFailedOnDebug().Failed) + if (PInvoke.CoCreateInstance(factoryClsid, null, CLSCTX.CLSCTX_LOCAL_SERVER, out IStorageProviderStatusUISourceFactory? pStorageProviderStatusUISourceFactory).ThrowIfFailedOnDebug().Failed || + pStorageProviderStatusUISourceFactory is null) return (false, 0, 0); - var syncRootIdHString = new MarshalString.Pinnable(syncRootId); - fixed (char* pSyncRootIdHString = syncRootIdHString) - { - if (pStorageProviderStatusUISourceFactory.Get()->GetStatusUISource(syncRootIdHString.GetAbi(), pStorageProviderStatusUISource.GetAddressOf()).ThrowIfFailedOnDebug().Failed || - pStorageProviderStatusUISource.Get()->GetStatusUI(pStorageProviderStatusUI.GetAddressOf()).ThrowIfFailedOnDebug().Failed || - pStorageProviderStatusUI.Get()->GetQuotaUI(pStorageProviderQuotaUI.GetAddressOf()).ThrowIfFailedOnDebug().Failed || - pStorageProviderQuotaUI.Get()->GetQuotaTotalInBytes(&ulTotalSize).ThrowIfFailedOnDebug().Failed || - pStorageProviderQuotaUI.Get()->GetQuotaUsedInBytes(&ulUsedSize).ThrowIfFailedOnDebug().Failed) - return (false, 0, 0); - } + if (pStorageProviderStatusUISourceFactory.GetStatusUISource(syncRootId, out IStorageProviderStatusUISource pStorageProviderStatusUISource).ThrowIfFailedOnDebug().Failed || + pStorageProviderStatusUISource.GetStatusUI(out IStorageProviderStatusUI pStorageProviderStatusUI).ThrowIfFailedOnDebug().Failed || + pStorageProviderStatusUI.GetQuotaUI(out IStorageProviderQuotaUI pStorageProviderQuotaUI).ThrowIfFailedOnDebug().Failed || + pStorageProviderQuotaUI.GetQuotaTotalInBytes(out ulTotalSize).ThrowIfFailedOnDebug().Failed || + pStorageProviderQuotaUI.GetQuotaUsedInBytes(out ulUsedSize).ThrowIfFailedOnDebug().Failed) + return (false, 0, 0); return (true, ulTotalSize, ulUsedSize); } diff --git a/src/Files.App/Utils/Taskbar/SystemTrayIconWindow.cs b/src/Files.App/Utils/Taskbar/SystemTrayIconWindow.cs index 532ed33baf5f..a8256c52dacd 100644 --- a/src/Files.App/Utils/Taskbar/SystemTrayIconWindow.cs +++ b/src/Files.App/Utils/Taskbar/SystemTrayIconWindow.cs @@ -5,6 +5,7 @@ using Windows.Win32; using Windows.Win32.Foundation; using Windows.Win32.UI.WindowsAndMessaging; +using WNDPROC = Windows.Win32.Extras.ManagedWNDPROC; namespace Files.App.Utils.Taskbar { diff --git a/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs b/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs index d7dbab8ae08f..f105eaee105e 100644 --- a/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Previews/ShellPreviewViewModel.cs @@ -1,4 +1,4 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. using Files.App.Helpers; @@ -19,6 +19,7 @@ using Windows.Win32.UI.Shell; using Windows.Win32.UI.WindowsAndMessaging; using WinRT; +using WNDPROC = Windows.Win32.Extras.ManagedWNDPROC; #pragma warning disable CS8305 // Type is for evaluation purposes only and is subject to change or removal in future updates. @@ -186,45 +187,46 @@ private unsafe bool ChildWindowToXaml(nint parent, UIElement presenter) ]; HRESULT hr = default; - Guid IID_IDCompositionDevice = typeof(IDCompositionDevice).GUID; - using ComPtr pD3D11Device = default; - using ComPtr pD3D11DeviceContext = default; - using ComPtr pDXGIDevice = default; - using ComPtr pDCompositionDevice = default; - using ComPtr pControlSurface = default; - ComPtr pChildVisual = default; // Don't dispose this one, it's used by the compositor + ID3D11Device? pD3D11Device = null; + ID3D11DeviceContext? pD3D11DeviceContext = null; + IDXGIDevice? pDXGIDevice = null; + object? pControlSurface = null; + IDCompositionVisual? pChildVisual = null; // Create the D3D11 device foreach (var driverType in driverTypes) { hr = PInvoke.D3D11CreateDevice( - null, driverType, new(nint.Zero), + null!, driverType, new(nint.Zero), D3D11_CREATE_DEVICE_FLAG.D3D11_CREATE_DEVICE_BGRA_SUPPORT, - null, /* FeatureLevels */ 0, /* SDKVersion */ 7, - pD3D11Device.GetAddressOf(), null, - pD3D11DeviceContext.GetAddressOf()); + ReadOnlySpan.Empty, /* SDKVersion */ 7, + out pD3D11Device, + out pD3D11DeviceContext); if (hr.Succeeded) break; } - if (pD3D11Device.IsNull) + if (pD3D11Device is null) return false; // Create the DComp device - pDXGIDevice.Attach((IDXGIDevice*)pD3D11Device.Get()); - hr = PInvoke.DCompositionCreateDevice( - pDXGIDevice.Get(), - &IID_IDCompositionDevice, - (void**)pDCompositionDevice.GetAddressOf()); + pDXGIDevice = (IDXGIDevice)pD3D11Device; + hr = PInvoke.DCompositionCreateDevice(pDXGIDevice, out IDCompositionDevice pDCompositionDevice); if (hr.Failed) return false; // Create the visual - hr = pDCompositionDevice.Get()->CreateVisual(pChildVisual.GetAddressOf()); - hr = pDCompositionDevice.Get()->CreateSurfaceFromHwnd(_hWnd, pControlSurface.GetAddressOf()); - hr = pChildVisual.Get()->SetContent(pControlSurface.Get()); - if (pChildVisual.IsNull || pControlSurface.IsNull) + hr = pDCompositionDevice.CreateVisual(out pChildVisual); + if (hr.Failed) + return false; + + hr = pDCompositionDevice.CreateSurfaceFromHwnd(_hWnd, out pControlSurface); + if (hr.Failed) + return false; + + hr = pChildVisual.SetContent(pControlSurface); + if (hr.Failed || pChildVisual is null || pControlSurface is null) return false; // Get the compositor and set the visual on it @@ -232,14 +234,14 @@ private unsafe bool ChildWindowToXaml(nint parent, UIElement presenter) _contentExternalOutputLink = ContentExternalOutputLink.Create(compositor); var target = _contentExternalOutputLink.As(); - target.SetRoot((nint)pChildVisual.Get()); + target.SetRoot(pChildVisual); _contentExternalOutputLink.PlacementVisual.Size = new(0, 0); _contentExternalOutputLink.PlacementVisual.Scale = new(1 / (float)presenter.XamlRoot.RasterizationScale); ElementCompositionPreview.SetElementChildVisual(presenter, _contentExternalOutputLink.PlacementVisual); // Commit the all pending DComp commands - pDCompositionDevice.Get()->Commit(); + pDCompositionDevice.Commit(); var dwAttrib = Convert.ToUInt32(true); diff --git a/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs b/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs index 1771114901a9..bf91920ce406 100644 --- a/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs @@ -1,4 +1,4 @@ -// Copyright (c) Files Community +// Copyright (c) Files Community // Licensed under the MIT License. using Microsoft.UI.Input; @@ -230,25 +230,20 @@ public override async Task ExecutePinToSidebarCommand(WidgetCardItem? item) if (currentPinnedItemIndex is -1) return; - HRESULT hr = default; - using ComPtr pAgileReference = default; - - unsafe - { - hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, IID.IID_IShellItem, (IUnknown*)folderCardItem.Item.ThisPtr, pAgileReference.GetAddressOf()); - } + HRESULT hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, typeof(IShellItem).GUID, folderCardItem.Item.ThisPtr, out IAgileReference pAgileReference); + if (hr.ThrowIfFailedOnDebug().Failed) + return; // Pin to Quick Access on Windows hr = await STATask.Run(() => { - unsafe - { - 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"); - } + hr = pAgileReference.Resolve(out IShellItem pShellItem); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; + + 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"); }, App.Logger); // The file watcher will update the collection automatically @@ -259,27 +254,22 @@ public override async Task ExecuteUnpinFromSidebarCommand(WidgetCardItem? item) if (item is not WidgetFolderCardItem folderCardItem || folderCardItem.Path is null) return; - HRESULT hr = default; - using ComPtr pAgileReference = default; - - unsafe - { - hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, IID.IID_IShellItem, (IUnknown*)folderCardItem.Item.ThisPtr, pAgileReference.GetAddressOf()); - } + HRESULT hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, typeof(IShellItem).GUID, folderCardItem.Item.ThisPtr, out IAgileReference pAgileReference); + if (hr.ThrowIfFailedOnDebug().Failed) + return; // Unpin from Quick Access on Windows hr = await STATask.Run(() => { - unsafe - { - IShellItem* pShellItem = null; - hr = pAgileReference.Get()->Resolve(IID.IID_IShellItem, (void**)&pShellItem); - using var windowsFile = new WindowsFile(pShellItem); + hr = pAgileReference.Resolve(out IShellItem pShellItem); + if (hr.ThrowIfFailedOnDebug().Failed) + return hr; - // 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 - return windowsFile.TryInvokeContextMenuVerbs(["unpinfromhome", "remove"], true); - } + 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 + return windowsFile.TryInvokeContextMenuVerbs(["unpinfromhome", "remove"], true); }, App.Logger); if (hr.ThrowIfFailedOnDebug().Failed) diff --git a/src/Files.Core.SourceGenerator/Generators/VTableFunctionGenerator.cs b/src/Files.Core.SourceGenerator/Generators/VTableFunctionGenerator.cs deleted file mode 100644 index 1c852cb5f2cf..000000000000 --- a/src/Files.Core.SourceGenerator/Generators/VTableFunctionGenerator.cs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) Files Community -// SPDX-License-Identifier: MPL-2.0 - -namespace Files.Core.SourceGenerator.Generators -{ - [Generator(LanguageNames.CSharp)] - internal class VTableFunctionGenerator : IIncrementalGenerator - { - public void Initialize(IncrementalGeneratorInitializationContext context) - { - var sources = context.SyntaxProvider.ForAttributeWithMetadataName( - "Files.Shared.Attributes.GeneratedVTableFunctionAttribute", - static (node, token) => - { - token.ThrowIfCancellationRequested(); - - // Check if the method has partial modifier and is public or internal (and not static) - if (node is not MethodDeclarationSyntax { AttributeLists.Count: > 0 } method || - !method.Modifiers.Any(SyntaxKind.PartialKeyword) || - !(method.Modifiers.Any(SyntaxKind.PublicKeyword) || method.Modifiers.Any(SyntaxKind.InternalKeyword)) || - method.Modifiers.Any(SyntaxKind.StaticKeyword)) - return false; - - // Check if the type containing the method has partial modifier and is a struct - if (node.Parent is not TypeDeclarationSyntax { Keyword.RawKind: (int)SyntaxKind.StructKeyword, Modifiers: { } modifiers } || - !modifiers.Any(SyntaxKind.PartialKeyword)) - return false; - - return true; - }, - static (context, token) => - { - token.ThrowIfCancellationRequested(); - - var fullyQualifiedParentTypeName = context.TargetSymbol.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - var structNamespace = context.TargetSymbol.ContainingType.ContainingNamespace.ToString(); - var structName = context.TargetSymbol.ContainingType.Name; - var methodSymbol = (IMethodSymbol)context.TargetSymbol; - var isReturnTypeVoid = methodSymbol.ReturnsVoid; - var functionName = methodSymbol.Name; - var returnTypeName = methodSymbol.ReturnType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); - var parameters = methodSymbol.Parameters.Select(x => new ParameterTypeNamePair(x.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), x.Name)); - var index = (int)context.Attributes[0].NamedArguments.FirstOrDefault(x => x.Key.Equals("Index")).Value.Value!; - - return new VTableFunctionInfo(fullyQualifiedParentTypeName, structNamespace, structName, isReturnTypeVoid, functionName, returnTypeName, index, new(parameters.ToImmutableArray())); - }) - .Where(static item => item is not null) - .Collect() - .Select((items, token) => - { - token.ThrowIfCancellationRequested(); - - return items.GroupBy(source => source.FullyQualifiedParentTypeName, StringComparer.OrdinalIgnoreCase).ToImmutableArray(); - }); - - - context.RegisterSourceOutput(sources, (context, sources) => - { - foreach (var source in sources) - { - var fileName = $"{source.ToImmutableArray().ElementAt(0).ParentTypeNamespace}.{source.ToImmutableArray().ElementAt(0).ParentTypeName}_VTableFunctions.g.cs"; - var generatedCSharpCode = GenerateVtableFunctionsForStruct(source.ToImmutableArray()); - - context.AddSource(fileName, generatedCSharpCode); - } - }); - } - - private string GenerateVtableFunctionsForStruct(ImmutableArray sources) - { - StringBuilder builder = new(); - - builder.AppendLine($"// "); - builder.AppendLine(); - builder.AppendLine($"using global::System.Runtime.CompilerServices;"); - builder.AppendLine(); - builder.AppendLine($"#pragma warning disable"); - builder.AppendLine(); - - builder.AppendLine($"namespace {sources.ElementAt(0).ParentTypeNamespace};"); - builder.AppendLine(); - - builder.AppendLine($"public unsafe partial struct {sources.ElementAt(0).ParentTypeName}"); - builder.AppendLine($"{{"); - - builder.AppendLine($" private void** lpVtbl;"); - builder.AppendLine(); - - var sourceIndex = 0; - var sourceCount = sources.Count(); - - foreach (var source in sources) - { - var returnTypeName = source.IsReturnTypeVoid ? "void" : "int"; - - builder.AppendLine($" [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); - - builder.AppendLine($" public partial {source.ReturnTypeName} {source.Name}({string.Join(", ", source.Parameters.Select(x => $"{x.FullyQualifiedTypeName} {x.ValueName}"))})"); - builder.AppendLine($" {{"); - builder.AppendLine($" return ({source.ReturnTypeName})((delegate* unmanaged[MemberFunction]<{sources.ElementAt(0).FullyQualifiedParentTypeName}*, {string.Join(", ", source.Parameters.Select(x => $"{x.FullyQualifiedTypeName}"))}, {returnTypeName}>)(lpVtbl[{source.Index}]))"); - builder.AppendLine($" (({sources.ElementAt(0).FullyQualifiedParentTypeName}*)global::System.Runtime.CompilerServices.Unsafe.AsPointer(ref this), {string.Join(", ", source.Parameters.Select(x => $"{x.ValueName}"))});"); - builder.AppendLine($" }}"); - - if (sourceIndex < sourceCount - 1) - builder.AppendLine(); - - sourceIndex++; - } - - builder.AppendLine($"}}"); - - return builder.ToString(); - } - } -} diff --git a/src/Files.Shared/Attributes/GeneratedVTableFunctionAttribute.cs b/src/Files.Shared/Attributes/GeneratedVTableFunctionAttribute.cs deleted file mode 100644 index d957a05c3e5f..000000000000 --- a/src/Files.Shared/Attributes/GeneratedVTableFunctionAttribute.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Files Community -// SPDX-License-Identifier: MPL-2.0 - -using System; - -namespace Files.Shared.Attributes; - -[AttributeUsage(AttributeTargets.Method, Inherited = false)] -public sealed class GeneratedVTableFunctionAttribute : Attribute -{ - public required int Index { get; init; } -}