Skip to content

Commit cf2b38c

Browse files
Use ComPtr in sync root quota.
1 parent 5f5d723 commit cf2b38c

File tree

5 files changed

+155
-72
lines changed

5 files changed

+155
-72
lines changed

global.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/Files.App.CsWin32/Windows.Win32.ComPtr.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace Windows.Win32
1212
/// <summary>
1313
/// Contains a COM pointer and a set of methods to work with the pointer safely.
1414
/// </summary>
15-
public unsafe struct ComPtr<T> : IDisposable where T : unmanaged
15+
public unsafe struct ComPtr<T> : IDisposable where T : unmanaged, IComIID
1616
{
1717
private T* _ptr;
1818

@@ -40,11 +40,10 @@ public ComPtr(T* ptr)
4040
}
4141

4242
[MethodImpl(MethodImplOptions.AggressiveInlining)]
43-
public readonly ComPtr<U> As<U>() where U : unmanaged
43+
public readonly ComPtr<U> As<U>() where U : unmanaged, IComIID
4444
{
4545
ComPtr<U> ptr = default;
46-
Guid iid = typeof(U).GUID;
47-
((IUnknown*)_ptr)->QueryInterface(&iid, (void**)ptr.GetAddressOf());
46+
((IUnknown*)_ptr)->QueryInterface((Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in U.Guid)), (void**)ptr.GetAddressOf());
4847
return ptr;
4948
}
5049

src/Files.App/Utils/Storage/Helpers/DriveHelpers.cs

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,18 @@
22
// Licensed under the MIT License.
33

44
using DiscUtils.Udf;
5-
using Files.App.Services.SizeProvider;
65
using Microsoft.Management.Infrastructure;
7-
using Microsoft.Win32;
8-
using System.Runtime.InteropServices;
96
using Windows.Devices.Enumeration;
107
using Windows.Devices.Portable;
118
using Windows.Storage;
129
using Windows.Storage.FileProperties;
13-
using Windows.Storage.Provider;
1410
using Windows.Win32;
1511
using Windows.Win32.Foundation;
16-
using WinRT;
1712

1813
namespace Files.App.Utils.Storage
1914
{
2015
public static class DriveHelpers
2116
{
22-
private static readonly Guid IID_IStorageProviderStatusUISourceFactory = new Guid("12e46b74-4e5a-58d1-a62f-0376e8ee7dd8");
23-
2417
public static async void EjectDeviceAsync(string path)
2518
{
2619
await ContextMenu.InvokeVerb("eject", path);
@@ -175,60 +168,5 @@ public static async Task<StorageItemThumbnail> GetThumbnailAsync(StorageFolder f
175168
=> (StorageItemThumbnail)await FilesystemTasks.Wrap(()
176169
=> folder.GetThumbnailAsync(ThumbnailMode.SingleItem, 40, ThumbnailOptions.UseCurrentScale).AsTask()
177170
);
178-
179-
public static async Task<(bool Success, ulong Capacity, ulong Used)> GetSyncRootQuotaAsync(string path)
180-
{
181-
Windows.Storage.StorageFolder folder = await Windows.Storage.StorageFolder.GetFolderFromPathAsync(path);
182-
StorageProviderSyncRootInfo? syncRootInfo = null;
183-
184-
try
185-
{
186-
syncRootInfo = StorageProviderSyncRootManager.GetSyncRootInformationForFolder(folder);
187-
}
188-
catch
189-
{
190-
return (false, 0, 0);
191-
}
192-
193-
RegistryKey? key;
194-
if ((key = Registry.LocalMachine.OpenSubKey($"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SyncRootManager\\{syncRootInfo.Id}")) is null)
195-
{
196-
return (false, 0, 0);
197-
}
198-
199-
using (key)
200-
{
201-
if (key.GetValue("StorageProviderStatusUISourceFactory") is string statusUIclass)
202-
{
203-
StorageProviderStatusUI statusUI;
204-
205-
unsafe
206-
{
207-
if (PInvoke.CoCreateInstance(Guid.Parse(statusUIclass), null, Windows.Win32.System.Com.CLSCTX.CLSCTX_LOCAL_SERVER, IID_IStorageProviderStatusUISourceFactory, out void* statusUISourceFactoryAbi) != 0)
208-
{
209-
return (false, 0, 0);
210-
}
211-
212-
// CsWinRT wrappers won't work.
213-
// TODO: look to replace MarshalString with MarshalString.Pinnable?
214-
215-
nint statusUISourceAbi = 0;
216-
nint syncRootIdHstring = MarshalString.FromManaged(syncRootInfo.Id);
217-
nint statusUIAbi = 0;
218-
ExceptionHelpers.ThrowExceptionForHR(((delegate* unmanaged[MemberFunction]<IntPtr, IntPtr, IntPtr*, int>)(*(IntPtr*)((nint)(*(IntPtr*)statusUISourceFactoryAbi) + (nint)6 * (nint)sizeof(delegate* unmanaged[Stdcall]<IntPtr, IntPtr, IntPtr*, int>))))((nint)statusUISourceFactoryAbi, syncRootIdHstring, &statusUISourceAbi));
219-
ExceptionHelpers.ThrowExceptionForHR(((delegate* unmanaged[MemberFunction]<nint, nint*, int>)(*(IntPtr*)((nint)(*(IntPtr*)statusUISourceAbi) + (nint)6 * (nint)sizeof(delegate* unmanaged[Stdcall]<nint, nint*, int>))))(statusUISourceAbi, &statusUIAbi));
220-
statusUI = StorageProviderStatusUI.FromAbi(statusUIAbi);
221-
Marshal.Release(statusUISourceAbi);
222-
Marshal.Release((nint)statusUISourceFactoryAbi);
223-
MarshalString.DisposeAbi(statusUISourceAbi);
224-
}
225-
return (true, statusUI.QuotaUI.QuotaTotalInBytes, statusUI.QuotaUI.QuotaUsedInBytes);
226-
}
227-
else
228-
{
229-
return (false, 0, 0);
230-
}
231-
}
232-
}
233171
}
234172
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
using Microsoft.Win32;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
using Windows.Storage.Provider;
5+
using Windows.Win32;
6+
using Windows.Win32.Foundation;
7+
using WinRT;
8+
9+
namespace Files.App.Utils.Storage
10+
{
11+
internal static class SyncRootHelpers
12+
{
13+
private unsafe struct IStorageProviderStatusUISourceFactory : IComIID
14+
{
15+
private void** vtbl;
16+
17+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
18+
public HRESULT GetStatusUISource(nint syncRootId, IStorageProviderStatusUISource** result)
19+
{
20+
return ((delegate* unmanaged[Stdcall]<IStorageProviderStatusUISourceFactory*, nint, IStorageProviderStatusUISource**, HRESULT>)vtbl[6])((IStorageProviderStatusUISourceFactory*)Unsafe.AsPointer(ref this), syncRootId, result);
21+
}
22+
23+
public static ref readonly Guid Guid
24+
{
25+
get
26+
{
27+
// 12e46b74-4e5a-58d1-a62f-0376e8ee7dd8
28+
ReadOnlySpan<byte> data = new byte[]
29+
{
30+
0x74, 0x6b, 0xe4, 0x12,
31+
0x5a, 0x4e,
32+
0xd1, 0x58,
33+
0xa6, 0x2f,
34+
0x03, 0x76, 0xe8, 0xee, 0x7d, 0xd8
35+
};
36+
Debug.Assert(data.Length == sizeof(Guid));
37+
return ref Unsafe.As<byte, Guid>(ref MemoryMarshal.GetReference(data));
38+
}
39+
}
40+
}
41+
42+
private unsafe struct IStorageProviderStatusUISource : IComIID
43+
{
44+
private void** vtbl;
45+
46+
public HRESULT GetStatusUI(IStorageProviderStatusUI** result)
47+
{
48+
return ((delegate* unmanaged[Stdcall]<IStorageProviderStatusUISource*, IStorageProviderStatusUI**, HRESULT>)vtbl[6])((IStorageProviderStatusUISource*)Unsafe.AsPointer(ref this), result);
49+
}
50+
51+
public static ref readonly Guid Guid
52+
{
53+
get
54+
{
55+
// a306c249-3d66-5e70-9007-e43df96051ff
56+
ReadOnlySpan<byte> data = new byte[]
57+
{
58+
0x49, 0xc2, 0x06, 0xa3,
59+
0x66, 0x3d,
60+
0x70, 0x5e,
61+
0x90, 0x07,
62+
0xe4, 0x3d, 0xf9, 0x60, 0x51, 0xff
63+
};
64+
Debug.Assert(data.Length == sizeof(Guid));
65+
return ref Unsafe.As<byte, Guid>(ref MemoryMarshal.GetReference(data));
66+
}
67+
}
68+
}
69+
70+
private unsafe struct IStorageProviderStatusUI : IComIID
71+
{
72+
public static ref readonly Guid Guid
73+
{
74+
get
75+
{
76+
// d6b6a758-198d-5b80-977f-5ff73da33118
77+
ReadOnlySpan<byte> data = new byte[]
78+
{
79+
0x58, 0xa7, 0xb6, 0xd6,
80+
0x8d, 0x19,
81+
0x80, 0x5b,
82+
0x97, 0x7f,
83+
0x5f, 0xf7, 0x3d, 0xa3, 0x31, 0x18
84+
};
85+
Debug.Assert(data.Length == sizeof(Guid));
86+
return ref Unsafe.As<byte, Guid>(ref MemoryMarshal.GetReference(data));
87+
}
88+
}
89+
}
90+
91+
private static unsafe (bool Success, ulong Capacity, ulong Used) GetSyncRootQuotaFromSyncRootId(string syncRootId)
92+
{
93+
RegistryKey? key;
94+
if ((key = Registry.LocalMachine.OpenSubKey($"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SyncRootManager\\{syncRootId}")) is null)
95+
{
96+
return (false, 0, 0);
97+
}
98+
99+
using (key)
100+
{
101+
if (key.GetValue("StorageProviderStatusUISourceFactory") is string statusUIclass)
102+
{
103+
StorageProviderStatusUI statusUI;
104+
using (ComPtr<IStorageProviderStatusUISourceFactory> sourceFactoryNative = default)
105+
{
106+
Guid statusUIclassGuid = Guid.Parse(statusUIclass);
107+
if (PInvoke.CoCreateInstance(&statusUIclassGuid, null, Windows.Win32.System.Com.CLSCTX.CLSCTX_LOCAL_SERVER, (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IStorageProviderStatusUISourceFactory.Guid)), (void**)sourceFactoryNative.GetAddressOf()) != 0)
108+
{
109+
return (false, 0, 0);
110+
}
111+
112+
MarshalString.Pinnable syncRootIdHstring = new(syncRootId);
113+
fixed (char* ptr = syncRootIdHstring)
114+
using (ComPtr<IStorageProviderStatusUISource> sourceNative = default)
115+
{
116+
ExceptionHelpers.ThrowExceptionForHR(sourceFactoryNative.Get()->GetStatusUISource(syncRootIdHstring.GetAbi(), sourceNative.GetAddressOf()));
117+
118+
using (ComPtr<IStorageProviderStatusUI> statusNative = default)
119+
{
120+
ExceptionHelpers.ThrowExceptionForHR(sourceNative.Get()->GetStatusUI(statusNative.GetAddressOf()));
121+
statusUI = StorageProviderStatusUI.FromAbi((nint)statusNative.Get());
122+
}
123+
}
124+
}
125+
return (true, statusUI.QuotaUI.QuotaTotalInBytes, statusUI.QuotaUI.QuotaUsedInBytes);
126+
}
127+
else
128+
{
129+
return (false, 0, 0);
130+
}
131+
}
132+
}
133+
134+
public static async Task<(bool Success, ulong Capacity, ulong Used)> GetSyncRootQuotaAsync(string path)
135+
{
136+
Windows.Storage.StorageFolder folder = await Windows.Storage.StorageFolder.GetFolderFromPathAsync(path);
137+
StorageProviderSyncRootInfo? syncRootInfo = null;
138+
139+
try
140+
{
141+
syncRootInfo = StorageProviderSyncRootManager.GetSyncRootInformationForFolder(folder);
142+
}
143+
catch
144+
{
145+
return (false, 0, 0);
146+
}
147+
148+
return GetSyncRootQuotaFromSyncRootId(syncRootInfo.Id);
149+
}
150+
}
151+
}

src/Files.App/ViewModels/Properties/Items/DriveProperties.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public async override Task GetSpecialPropertiesAsync()
7878
return;
7979
}
8080

81-
var syncRootStatus = await DriveHelpers.GetSyncRootQuotaAsync(Drive.Path);
81+
var syncRootStatus = await SyncRootHelpers.GetSyncRootQuotaAsync(Drive.Path);
8282
if (syncRootStatus.Success)
8383
{
8484
ViewModel.DriveCapacityValue = syncRootStatus.Capacity;

0 commit comments

Comments
 (0)