Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit a9ef2a8

Browse files
committedJun 14, 2025·
Init
1 parent ba53823 commit a9ef2a8

File tree

14 files changed

+142
-188
lines changed

14 files changed

+142
-188
lines changed
 

‎src/Files.App.CsWin32/ComPtr`1.cs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,6 @@ public void Attach(T* other)
6464
return (T**)Unsafe.AsPointer(ref Unsafe.AsRef(in this));
6565
}
6666

67-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
68-
[Obsolete("Use `HRESULT As<U>(U** other)` instead.")]
69-
public readonly ComPtr<U> As<U>() where U : unmanaged, IComIID
70-
{
71-
ComPtr<U> ptr = default;
72-
((IUnknown*)_ptr)->QueryInterface((Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in U.Guid)), (void**)ptr.GetAddressOf());
73-
return ptr;
74-
}
75-
7667
[MethodImpl(MethodImplOptions.AggressiveInlining)]
7768
public readonly HRESULT As<U>(U** other) where U : unmanaged, IComIID
7869
{
@@ -91,22 +82,6 @@ public readonly HRESULT CoCreateInstance(Guid* rclsid, IUnknown* pUnkOuter = nul
9182
return PInvoke.CoCreateInstance(rclsid, pUnkOuter, dwClsContext, (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in T.Guid)), (void**)this.GetAddressOf());
9283
}
9384

94-
// Conversion operators
95-
96-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
97-
public static implicit operator ComPtr<T>(T* other)
98-
{
99-
ComPtr<T> ptr = default;
100-
ptr.Attach(other);
101-
return ptr;
102-
}
103-
104-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
105-
public static implicit operator T*(ComPtr<T> other)
106-
{
107-
return other._ptr;
108-
}
109-
11085
// Disposer
11186

11287
[MethodImpl(MethodImplOptions.AggressiveInlining)]

‎src/Files.App.CsWin32/ManualGuid.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ public static Guid* IID_IStorageProviderStatusUISourceFactory
4141

4242
[GuidRVAGen.Guid("00021500-0000-0000-C000-000000000046")]
4343
public static partial Guid* IID_IQueryInfo { get; }
44+
45+
[GuidRVAGen.Guid("BCC18B79-BA16-442F-80C4-8A59C30C463B")]
46+
public static partial Guid* IID_IShellItemImageFactory { get; }
47+
48+
[GuidRVAGen.Guid("000214F9-0000-0000-C000-000000000046")]
49+
public static partial Guid* IID_IShellLinkW { get; }
4450
}
4551

4652
public static unsafe partial class CLSID

‎src/Files.App.CsWin32/NativeMethods.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,4 @@ QITIPF_FLAGS
225225
GetKeyboardState
226226
MapVirtualKey
227227
GetKeyboardLayout
228+
S_FALSE

‎src/Files.App.Storage/Storables/HomeFolder/HomeFolder.cs

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace Files.App.Storage.Storables
99
{
10-
public partial class HomeFolder : IHomeFolder
10+
public unsafe partial class HomeFolder : IHomeFolder
1111
{
1212
public string Id => "Home"; // Will be "files://Home" in the future.
1313

@@ -48,38 +48,36 @@ public IAsyncEnumerable<IStorableChild> GetQuickAccessFolderAsync(CancellationTo
4848
/// <inheritdoc/>
4949
public IAsyncEnumerable<IStorableChild> GetLogicalDrivesAsync(CancellationToken cancellationToken = default)
5050
{
51-
return GetLogicalDrives().ToAsyncEnumerable();
51+
var availableDrives = PInvoke.GetLogicalDrives();
52+
if (availableDrives is 0)
53+
return Enumerable.Empty<IStorableChild>().ToAsyncEnumerable();
5254

53-
IEnumerable<IStorableChild> GetLogicalDrives()
54-
{
55-
var availableDrives = PInvoke.GetLogicalDrives();
56-
if (availableDrives is 0)
57-
yield break;
58-
59-
int count = BitOperations.PopCount(availableDrives);
60-
var driveLetters = new char[count];
55+
int count = BitOperations.PopCount(availableDrives);
56+
var driveLetters = new char[count];
6157

62-
count = 0;
63-
char driveLetter = 'A';
64-
while (availableDrives is not 0)
65-
{
66-
if ((availableDrives & 1) is not 0)
67-
driveLetters[count++] = driveLetter;
58+
count = 0;
59+
char driveLetter = 'A';
60+
while (availableDrives is not 0)
61+
{
62+
if ((availableDrives & 1) is not 0)
63+
driveLetters[count++] = driveLetter;
6864

69-
availableDrives >>= 1;
70-
driveLetter++;
71-
}
65+
availableDrives >>= 1;
66+
driveLetter++;
67+
}
7268

73-
foreach (char letter in driveLetters)
74-
{
75-
cancellationToken.ThrowIfCancellationRequested();
69+
List<IStorableChild> driveItems = [];
70+
foreach (char letter in driveLetters)
71+
{
72+
cancellationToken.ThrowIfCancellationRequested();
7673

77-
if (WindowsStorable.TryParse($"{letter}:\\") is not IWindowsStorable driveRoot)
78-
throw new InvalidOperationException();
74+
if (WindowsStorable.TryParse($"{letter}:\\") is not IWindowsStorable driveRoot)
75+
throw new InvalidOperationException();
7976

80-
yield return new WindowsFolder(driveRoot.ThisPtr);
81-
}
77+
driveItems.Add(new WindowsFolder(driveRoot.ThisPtr));
8278
}
79+
80+
return driveItems.ToAsyncEnumerable();
8381
}
8482

8583
/// <inheritdoc/>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
namespace Files.App.Storage
5+
{
6+
public interface IWindowsFile : IWindowsStorable, IChildFile
7+
{
8+
}
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright (c) Files Community
2+
// Licensed under the MIT License.
3+
4+
namespace Files.App.Storage
5+
{
6+
public interface IWindowsFolder : IWindowsStorable, IChildFolder
7+
{
8+
}
9+
}
Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
// Copyright (c) Files Community
22
// Licensed under the MIT License.
33

4-
using Windows.Win32;
54
using Windows.Win32.UI.Shell;
65

76
namespace Files.App.Storage
87
{
9-
public interface IWindowsStorable : IDisposable
8+
public unsafe interface IWindowsStorable : IStorableChild, IEquatable<IWindowsStorable>, IDisposable
109
{
11-
ComPtr<IShellItem> ThisPtr { get; }
10+
IShellItem* ThisPtr { get; }
1211
}
1312
}

‎src/Files.App.Storage/Storables/WindowsStorage/WindowsBulkOperations.cs

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ namespace Files.App.Storage
1212
/// <summary>
1313
/// Handles bulk file operations in Windows, such as copy, move, delete, create, and rename, supporting progress tracking and event notifications.
1414
/// </summary>
15-
public sealed partial class WindowsBulkOperations : IDisposable
15+
public unsafe partial class WindowsBulkOperations : IDisposable
1616
{
1717
// Fields
1818

19-
private readonly ComPtr<IFileOperation> _pFileOperation;
20-
private readonly ComPtr<IFileOperationProgressSink> _pProgressSink;
19+
private readonly IFileOperation* _pFileOperation;
20+
private readonly IFileOperationProgressSink* _pProgressSink;
2121
private readonly uint _progressSinkCookie;
2222

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

76-
HRESULT hr = PInvoke.CoCreateInstance(
77-
&clsid,
78-
null,
79-
CLSCTX.CLSCTX_LOCAL_SERVER,
80-
&iid,
81-
(void**)_pFileOperation.GetAddressOf())
82-
.ThrowIfFailedOnDebug();
75+
HRESULT hr = PInvoke.CoCreateInstance(CLSID.CLSID_FileOperation, null, CLSCTX.CLSCTX_LOCAL_SERVER, IID.IID_IFileOperation, (void**)&pFileOperation);
76+
hr.ThrowIfFailedOnDebug();
77+
78+
_pFileOperation = pFileOperation;
8379

8480
if (ownerHWnd != default)
85-
hr = _pFileOperation.Get()->SetOwnerWindow(ownerHWnd).ThrowIfFailedOnDebug();
81+
hr = _pFileOperation->SetOwnerWindow(ownerHWnd).ThrowIfFailedOnDebug();
8682

87-
hr = _pFileOperation.Get()->SetOperationFlags(flags).ThrowIfFailedOnDebug();
83+
hr = _pFileOperation->SetOperationFlags(flags).ThrowIfFailedOnDebug();
8884

89-
_pProgressSink.Attach((IFileOperationProgressSink*)WindowsBulkOperationsSink.Create(this));
90-
hr = _pFileOperation.Get()->Advise(_pProgressSink.Get(), out var progressSinkCookie).ThrowIfFailedOnDebug();
85+
_pProgressSink = (IFileOperationProgressSink*)WindowsBulkOperationsSink.Create(this);
86+
hr = _pFileOperation->Advise(_pProgressSink, out var progressSinkCookie).ThrowIfFailedOnDebug();
9187
_progressSinkCookie = progressSinkCookie;
9288
}
9389

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

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

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

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

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

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

165161
// Disposer
166162

167163
/// <inheritdoc/>
168164
public unsafe void Dispose()
169165
{
170-
if (!_pProgressSink.IsNull)
171-
_pFileOperation.Get()->Unadvise(_progressSinkCookie);
166+
if (_pProgressSink is not null)
167+
_pFileOperation->Unadvise(_progressSinkCookie);
172168

173-
_pFileOperation.Dispose();
174-
_pProgressSink.Dispose();
169+
_pFileOperation->Release();
170+
_pProgressSink->Release();
175171
}
176172
}
177173
}

‎src/Files.App.Storage/Storables/WindowsStorage/WindowsFile.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
namespace Files.App.Storage
99
{
1010
[DebuggerDisplay("{" + nameof(ToString) + "()}")]
11-
public sealed class WindowsFile : WindowsStorable, IChildFile
11+
public unsafe class WindowsFile : WindowsStorable, IWindowsFile
1212
{
13-
public WindowsFile(ComPtr<IShellItem> nativeObject)
13+
public WindowsFile(IShellItem* ptr)
1414
{
15-
ThisPtr = nativeObject;
15+
ThisPtr = ptr;
1616
}
1717

1818
public Task<Stream> OpenStreamAsync(FileAccess accessMode, CancellationToken cancellationToken = default)
Lines changed: 25 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Files Community
22
// Licensed under the MIT License.
33

4-
using System.Runtime.CompilerServices;
54
using Windows.Win32;
65
using Windows.Win32.Foundation;
76
using Windows.Win32.System.SystemServices;
@@ -10,80 +9,59 @@
109
namespace Files.App.Storage
1110
{
1211
[DebuggerDisplay("{" + nameof(ToString) + "()}")]
13-
public sealed class WindowsFolder : WindowsStorable, IChildFolder
12+
public unsafe class WindowsFolder : WindowsStorable, IWindowsFolder
1413
{
15-
public WindowsFolder(ComPtr<IShellItem> nativeObject)
14+
public WindowsFolder(IShellItem* ptr)
1615
{
17-
ThisPtr = nativeObject;
18-
}
19-
20-
public unsafe WindowsFolder(IShellItem* nativeObject)
21-
{
22-
ComPtr<IShellItem> ptr = default;
23-
ptr.Attach(nativeObject);
2416
ThisPtr = ptr;
2517
}
2618

27-
public unsafe WindowsFolder(Guid folderId)
19+
public WindowsFolder(Guid folderId)
2820
{
29-
ComPtr<IShellItem> pItem = default;
21+
IShellItem* pShellItem = default;
3022

31-
HRESULT hr = PInvoke.SHGetKnownFolderItem(&folderId, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, IID.IID_IShellItem, (void**)pItem.GetAddressOf());
23+
HRESULT hr = PInvoke.SHGetKnownFolderItem(&folderId, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, IID.IID_IShellItem, (void**)&pShellItem);
3224
if (hr.Failed)
3325
{
3426
fixed (char* pszShellPath = $"Shell:::{folderId:B}")
35-
hr = PInvoke.SHCreateItemFromParsingName(pszShellPath, null, IID.IID_IShellItem, (void**)pItem.GetAddressOf());
27+
hr = PInvoke.SHCreateItemFromParsingName(pszShellPath, null, IID.IID_IShellItem, (void**)&pShellItem);
3628

3729
// Invalid FOLDERID; this should never happen.
3830
hr.ThrowOnFailure();
3931
}
4032

41-
ThisPtr = pItem;
33+
ThisPtr = pShellItem;
4234
}
4335

4436
public IAsyncEnumerable<IStorableChild> GetItemsAsync(StorableType type = StorableType.All, CancellationToken cancellationToken = default)
4537
{
46-
return GetItems().ToAsyncEnumerable();
38+
using ComPtr<IEnumShellItems> pEnumShellItems = default;
4739

48-
unsafe IEnumerable<IStorableChild> GetItems()
49-
{
50-
ComPtr<IEnumShellItems> pEnumShellItems = default;
51-
GetEnumerator();
40+
HRESULT hr = ThisPtr->BindToHandler(null, BHID.BHID_EnumItems, IID.IID_IEnumShellItems, (void**)pEnumShellItems.GetAddressOf());
41+
if (hr.ThrowIfFailedOnDebug().Failed)
42+
return Enumerable.Empty<IStorableChild>().ToAsyncEnumerable();
5243

53-
ComPtr<IShellItem> pShellItem = default;
54-
while (GetNext() && !pShellItem.IsNull)
55-
{
56-
cancellationToken.ThrowIfCancellationRequested();
57-
var isFolder = pShellItem.HasShellAttributes(SFGAO_FLAGS.SFGAO_FOLDER);
44+
List<IStorableChild> childItems = [];
5845

59-
if (type is StorableType.File && !isFolder)
60-
{
61-
yield return new WindowsFile(pShellItem);
62-
}
63-
else if (type is StorableType.Folder && isFolder)
64-
{
65-
yield return new WindowsFolder(pShellItem);
66-
}
67-
else
68-
{
69-
continue;
70-
}
71-
}
72-
73-
yield break;
46+
IShellItem* pChildShellItem = null;
47+
while ((hr = pEnumShellItems.Get()->Next(1, &pChildShellItem)) == HRESULT.S_OK)
48+
{
49+
bool isFolder = pChildShellItem->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var dwAttributes).Succeeded && dwAttributes is SFGAO_FLAGS.SFGAO_FOLDER;
7450

75-
unsafe void GetEnumerator()
51+
if (type.HasFlag(StorableType.File) && !isFolder)
7652
{
77-
HRESULT hr = ThisPtr.Get()->BindToHandler(null, BHID.BHID_EnumItems, IID.IID_IEnumShellItems, (void**)pEnumShellItems.GetAddressOf());
78-
hr.ThrowIfFailedOnDebug();
53+
childItems.Add(new WindowsFile(pChildShellItem));
7954
}
80-
81-
unsafe bool GetNext()
55+
else if (type.HasFlag(StorableType.Folder) && isFolder)
8256
{
83-
HRESULT hr = pEnumShellItems.Get()->Next(1, pShellItem.GetAddressOf());
84-
return hr.ThrowIfFailedOnDebug() == HRESULT.S_OK;
57+
childItems.Add(new WindowsFolder(pChildShellItem));
8558
}
8659
}
60+
61+
if (hr.ThrowIfFailedOnDebug().Failed)
62+
return Enumerable.Empty<IStorableChild>().ToAsyncEnumerable();
63+
64+
return childItems.ToAsyncEnumerable();
8765
}
8866
}
8967
}

‎src/Files.App.Storage/Storables/WindowsStorage/WindowsStorable.cs

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,58 @@
11
// Copyright (c) Files Community
22
// Licensed under the MIT License.
33

4+
using System.Runtime.CompilerServices;
45
using Windows.Win32;
56
using Windows.Win32.Foundation;
67
using Windows.Win32.System.SystemServices;
78
using Windows.Win32.UI.Shell;
89

910
namespace Files.App.Storage
1011
{
11-
public abstract class WindowsStorable : IWindowsStorable, IStorableChild, IEquatable<IWindowsStorable>
12+
public unsafe abstract class WindowsStorable : IWindowsStorable
1213
{
13-
public ComPtr<IShellItem> ThisPtr { get; protected set; }
14+
public IShellItem* ThisPtr
15+
{
16+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
17+
get;
18+
19+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
20+
protected set;
21+
}
1422

1523
public string Id => this.GetDisplayName(SIGDN.SIGDN_FILESYSPATH);
1624

1725
public string Name => this.GetDisplayName(SIGDN.SIGDN_PARENTRELATIVEFORUI);
1826

19-
public static unsafe WindowsStorable? TryParse(string parsablePath)
27+
public static WindowsStorable? TryParse(string szPath)
2028
{
2129
HRESULT hr = default;
22-
ComPtr<IShellItem> pShellItem = default;
23-
var IID_IShellItem = typeof(IShellItem).GUID;
24-
25-
fixed (char* pszParsablePath = parsablePath)
26-
{
27-
hr = PInvoke.SHCreateItemFromParsingName(
28-
pszParsablePath,
29-
null,
30-
&IID_IShellItem,
31-
(void**)pShellItem.GetAddressOf());
32-
}
33-
34-
if (pShellItem.IsNull)
30+
IShellItem* pShellItem = null;
31+
32+
fixed (char* pszPath = szPath)
33+
hr = PInvoke.SHCreateItemFromParsingName(pszPath, null, IID.IID_IShellItem, (void**)&pShellItem);
34+
35+
if (pShellItem is null)
3536
return null;
3637

37-
return pShellItem.HasShellAttributes(SFGAO_FLAGS.SFGAO_FOLDER)
38-
? new WindowsFolder(pShellItem)
39-
: new WindowsFile(pShellItem);
38+
return TryParse(pShellItem);
4039
}
4140

42-
public static unsafe WindowsStorable? TryParse(IShellItem* ptr)
41+
public static WindowsStorable? TryParse(IShellItem* pShellItem)
4342
{
44-
ComPtr<IShellItem> pShellItem = default;
45-
pShellItem.Attach(ptr);
43+
bool isFolder = pShellItem->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var returnedAttributes).Succeeded && returnedAttributes == SFGAO_FLAGS.SFGAO_FOLDER;
4644

47-
return pShellItem.HasShellAttributes(SFGAO_FLAGS.SFGAO_FOLDER)
48-
? new WindowsFolder(pShellItem)
49-
: new WindowsFile(pShellItem);
45+
return isFolder ? new WindowsFolder(pShellItem) : new WindowsFile(pShellItem);
5046
}
5147

5248
public unsafe Task<IFolder?> GetParentAsync(CancellationToken cancellationToken = default)
5349
{
5450
cancellationToken.ThrowIfCancellationRequested();
5551

56-
ComPtr<IShellItem> pParentFolder = default;
57-
HRESULT hr = ThisPtr.Get()->GetParent(pParentFolder.GetAddressOf());
58-
if (hr.Failed)
59-
{
60-
if (!pParentFolder.IsNull) pParentFolder.Dispose();
61-
52+
IShellItem* pParentFolder = default;
53+
HRESULT hr = ThisPtr->GetParent(&pParentFolder);
54+
if (hr.ThrowIfFailedOnDebug().Failed)
6255
return Task.FromResult<IFolder?>(null);
63-
}
6456

6557
return Task.FromResult<IFolder?>(new WindowsFolder(pParentFolder));
6658
}
@@ -79,7 +71,7 @@ public override int GetHashCode()
7971
/// <inheritdoc/>
8072
public void Dispose()
8173
{
82-
ThisPtr.Dispose();
74+
ThisPtr->Release();
8375
}
8476

8577
/// <inheritdoc/>
@@ -94,7 +86,7 @@ public unsafe bool Equals(IWindowsStorable? other)
9486
if (other is null)
9587
return false;
9688

97-
return ThisPtr.Get()->Compare(other.ThisPtr.Get(), (uint)_SICHINTF.SICHINT_DISPLAY, out int order).Succeeded && order is 0;
89+
return ThisPtr->Compare(other.ThisPtr, (uint)_SICHINTF.SICHINT_DISPLAY, out int order).Succeeded && order is 0;
9890
}
9991

10092
public static bool operator ==(WindowsStorable left, WindowsStorable right)

‎src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Icon.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public unsafe static HRESULT TryGetThumbnail(this IWindowsStorable storable, int
4545
thumbnailData = null;
4646

4747
using ComPtr<IShellItemImageFactory> pShellItemImageFactory = default;
48-
storable.ThisPtr.As(pShellItemImageFactory.GetAddressOf());
48+
storable.ThisPtr->QueryInterface(IID.IID_IShellItemImageFactory, (void**)pShellItemImageFactory.GetAddressOf());
4949
if (pShellItemImageFactory.IsNull)
5050
return HRESULT.E_NOINTERFACE;
5151

@@ -267,10 +267,8 @@ public unsafe static HRESULT TrySetShortcutIcon(this IWindowsStorable storable,
267267
return HRESULT.E_INVALIDARG;
268268

269269
using ComPtr<IShellLinkW> pShellLink = default;
270-
Guid IID_IShellLink = IShellLinkW.IID_Guid;
271-
Guid BHID_SFUIObject = PInvoke.BHID_SFUIObject;
272270

273-
HRESULT hr = storable.ThisPtr.Get()->BindToHandler(null, &BHID_SFUIObject, &IID_IShellLink, (void**)pShellLink.GetAddressOf());
271+
HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IShellLinkW, (void**)pShellLink.GetAddressOf());
274272
if (hr.ThrowIfFailedOnDebug().Failed)
275273
return hr;
276274

‎src/Files.App.Storage/Storables/WindowsStorage/WindowsStorableHelpers.Shell.cs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public static partial class WindowsStorableHelpers
1717
public unsafe static HRESULT GetPropertyValue<TValue>(this IWindowsStorable storable, string propKey, out TValue value)
1818
{
1919
using ComPtr<IShellItem2> pShellItem2 = default;
20-
HRESULT hr = storable.ThisPtr.Get()->QueryInterface(IID.IID_IShellItem2, (void**)pShellItem2.GetAddressOf());
20+
HRESULT hr = storable.ThisPtr->QueryInterface(IID.IID_IShellItem2, (void**)pShellItem2.GetAddressOf());
2121

2222
PROPERTYKEY propertyKey = default;
2323
fixed (char* pszPropertyKey = propKey)
@@ -33,10 +33,9 @@ public unsafe static HRESULT GetPropertyValue<TValue>(this IWindowsStorable stor
3333
}
3434
if (typeof(TValue) == typeof(bool))
3535
{
36-
bool propertyValue = false;
37-
hr = pShellItem2.Get()->GetBool(propertyKey, out var fPropertyValue);
38-
propertyValue = fPropertyValue;
39-
value = Unsafe.As<bool, TValue>(ref propertyValue);
36+
bool fPropertyValue = false;
37+
hr = pShellItem2.Get()->GetBool(&propertyKey, (BOOL*)&fPropertyValue);
38+
value = Unsafe.As<bool, TValue>(ref fPropertyValue);
4039

4140
return hr;
4241
}
@@ -49,20 +48,14 @@ public unsafe static HRESULT GetPropertyValue<TValue>(this IWindowsStorable stor
4948

5049
public unsafe static bool HasShellAttributes(this IWindowsStorable storable, SFGAO_FLAGS attributes)
5150
{
52-
return storable.ThisPtr.Get()->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var returnedAttributes).Succeeded &&
53-
returnedAttributes == attributes;
54-
}
55-
56-
public unsafe static bool HasShellAttributes(this ComPtr<IShellItem> pShellItem, SFGAO_FLAGS attributes)
57-
{
58-
return pShellItem.Get()->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var returnedAttributes).Succeeded &&
51+
return storable.ThisPtr->GetAttributes(SFGAO_FLAGS.SFGAO_FOLDER, out var returnedAttributes).Succeeded &&
5952
returnedAttributes == attributes;
6053
}
6154

6255
public unsafe static string GetDisplayName(this IWindowsStorable storable, SIGDN options = SIGDN.SIGDN_FILESYSPATH)
6356
{
6457
using ComHeapPtr<PWSTR> pszName = default;
65-
HRESULT hr = storable.ThisPtr.Get()->GetDisplayName(options, (PWSTR*)pszName.GetAddressOf());
58+
HRESULT hr = storable.ThisPtr->GetDisplayName(options, (PWSTR*)pszName.GetAddressOf());
6659

6760
return hr.ThrowIfFailedOnDebug().Succeeded
6861
? new string((char*)pszName.Get()) // this is safe as it gets memcpy'd internally
@@ -74,7 +67,7 @@ public unsafe static HRESULT TryInvokeContextMenuVerb(this IWindowsStorable stor
7467
Debug.Assert(Thread.CurrentThread.GetApartmentState() is ApartmentState.STA);
7568

7669
using ComPtr<IContextMenu> pContextMenu = default;
77-
HRESULT hr = storable.ThisPtr.Get()->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf());
70+
HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf());
7871
HMENU hMenu = PInvoke.CreatePopupMenu();
7972
hr = pContextMenu.Get()->QueryContextMenu(hMenu, 0, 1, 0x7FFF, PInvoke.CMF_OPTIMIZEFORINVOKE);
8073

@@ -99,7 +92,7 @@ public unsafe static HRESULT TryInvokeContextMenuVerbs(this IWindowsStorable sto
9992
Debug.Assert(Thread.CurrentThread.GetApartmentState() is ApartmentState.STA);
10093

10194
using ComPtr<IContextMenu> pContextMenu = default;
102-
HRESULT hr = storable.ThisPtr.Get()->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf());
95+
HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf());
10396
HMENU hMenu = PInvoke.CreatePopupMenu();
10497
hr = pContextMenu.Get()->QueryContextMenu(hMenu, 0, 1, 0x7FFF, PInvoke.CMF_OPTIMIZEFORINVOKE);
10598

@@ -130,7 +123,7 @@ public unsafe static HRESULT TryGetShellTooltip(this IWindowsStorable storable,
130123
tooltip = null;
131124

132125
using ComPtr<IQueryInfo> pQueryInfo = default;
133-
HRESULT hr = storable.ThisPtr.Get()->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IQueryInfo, (void**)pQueryInfo.GetAddressOf());
126+
HRESULT hr = storable.ThisPtr->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IQueryInfo, (void**)pQueryInfo.GetAddressOf());
134127
if (hr.ThrowIfFailedOnDebug().Failed)
135128
return hr;
136129

‎src/Files.App/ViewModels/UserControls/Widgets/QuickAccessWidgetViewModel.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public override async Task ExecutePinToSidebarCommand(WidgetCardItem? item)
198198

199199
unsafe
200200
{
201-
hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, IID.IID_IShellItem, (IUnknown*)folderCardItem.Item.ThisPtr.Get(), pAgileReference.GetAddressOf());
201+
hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, IID.IID_IShellItem, (IUnknown*)folderCardItem.Item.ThisPtr, pAgileReference.GetAddressOf());
202202
}
203203

204204
// Pin to Quick Access on Windows
@@ -208,7 +208,7 @@ public override async Task ExecutePinToSidebarCommand(WidgetCardItem? item)
208208
{
209209
using ComPtr<IShellItem> pShellItem = default;
210210
hr = pAgileReference.Get()->Resolve(IID.IID_IShellItem, (void**)pShellItem.GetAddressOf());
211-
var windowsFile = new WindowsFile(pShellItem);
211+
var windowsFile = new WindowsFile(pShellItem.Get());
212212

213213
// NOTE: "pintohome" is an undocumented verb, which calls an undocumented COM class, windows.storage.dll!CPinToFrequentExecute : public IExecuteCommand, ...
214214
return windowsFile.TryInvokeContextMenuVerb("pintohome");
@@ -234,7 +234,7 @@ public override async Task ExecuteUnpinFromSidebarCommand(WidgetCardItem? item)
234234

235235
unsafe
236236
{
237-
hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, IID.IID_IShellItem, (IUnknown*)folderCardItem.Item.ThisPtr.Get(), pAgileReference.GetAddressOf());
237+
hr = PInvoke.RoGetAgileReference(AgileReferenceOptions.AGILEREFERENCE_DEFAULT, IID.IID_IShellItem, (IUnknown*)folderCardItem.Item.ThisPtr, pAgileReference.GetAddressOf());
238238
}
239239

240240
// Unpin from Quick Access on Windows
@@ -244,7 +244,7 @@ public override async Task ExecuteUnpinFromSidebarCommand(WidgetCardItem? item)
244244
{
245245
using ComPtr<IShellItem> pShellItem = default;
246246
hr = pAgileReference.Get()->Resolve(IID.IID_IShellItem, (void**)pShellItem.GetAddressOf());
247-
var windowsFile = new WindowsFile(pShellItem);
247+
var windowsFile = new WindowsFile(pShellItem.Get());
248248

249249
// NOTE: "unpinfromhome" is an undocumented verb, which calls an undocumented COM class, windows.storage.dll!CRemoveFromFrequentPlacesExecute : public IExecuteCommand, ...
250250
// NOTE: "remove" is for some shell folders where the "unpinfromhome" may not work

0 commit comments

Comments
 (0)
Please sign in to comment.