Skip to content

Commit 283345a

Browse files
Feature: Display OneDrive size on 23H2 (#16882)
Signed-off-by: Dongle <[email protected]>
1 parent edeba89 commit 283345a

File tree

4 files changed

+165
-5
lines changed

4 files changed

+165
-5
lines changed

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
"version": "9.0.200",
44
"rollForward": "latestMajor"
55
}
6-
}
6+
}

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

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: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public override void GetBaseProperties()
4545
public async override Task GetSpecialPropertiesAsync()
4646
{
4747
ViewModel.ItemAttributesVisibility = false;
48+
4849
var item = await FilesystemTasks.Wrap(() => DriveHelpers.GetRootFromPathAsync(Drive.Path));
4950
BaseStorageFolder diskRoot = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderFromPathAsync(Drive.Path, item));
5051

@@ -77,6 +78,15 @@ public async override Task GetSpecialPropertiesAsync()
7778
return;
7879
}
7980

81+
var syncRootStatus = await SyncRootHelpers.GetSyncRootQuotaAsync(Drive.Path);
82+
if (syncRootStatus.Success)
83+
{
84+
ViewModel.DriveCapacityValue = syncRootStatus.Capacity;
85+
ViewModel.DriveUsedSpaceValue = syncRootStatus.Used;
86+
ViewModel.DriveFreeSpaceValue = syncRootStatus.Capacity - syncRootStatus.Used;
87+
return;
88+
}
89+
8090
try
8191
{
8292
string freeSpace = "System.FreeSpace";

0 commit comments

Comments
 (0)