Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions src/Files.App.CsWin32/HRESULT.Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

using System.Runtime.InteropServices;
using Windows.Win32.Foundation;

namespace Windows.Win32
{
public static class HRESULTExtensions
{
/// <summary>
/// Throws an exception if the <see cref="HRESULT"/> indicates a failure in debug mode. Otherwise, it returns the original <see cref="HRESULT"/>.
/// </summary>
/// <param name="hr">Represents the result of an operation, indicating success or failure.</param>
/// <returns>Returns the original <see cref="HRESULT"/> value regardless of the operation's success.</returns>
public static HRESULT ThrowIfFailedOnDebug(this HRESULT hr)
{
#if DEBUG
if (hr.Failed)
Marshal.ThrowExceptionForHR(hr.Value);
#endif

return hr;
}
}
}
47 changes: 47 additions & 0 deletions src/Files.App.CsWin32/IStorageProviderQuotaUI.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Win32.Foundation;

namespace Windows.Win32.System.WinRT
{
public unsafe struct IStorageProviderQuotaUI : IComIID
{
private void** lpVtbl;

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderQuotaUI.cs

View workflow job for this annotation

GitHub Actions / build (Release, arm64)

Field 'IStorageProviderQuotaUI.lpVtbl' is never assigned to, and will always have its default value

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderQuotaUI.cs

View workflow job for this annotation

GitHub Actions / build (Debug, arm64)

Field 'IStorageProviderQuotaUI.lpVtbl' is never assigned to, and will always have its default value

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderQuotaUI.cs

View workflow job for this annotation

GitHub Actions / build (Debug, x64)

Field 'IStorageProviderQuotaUI.lpVtbl' is never assigned to, and will always have its default value

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderQuotaUI.cs

View workflow job for this annotation

GitHub Actions / build (Release, x64)

Field 'IStorageProviderQuotaUI.lpVtbl' is never assigned to, and will always have its default value

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HRESULT GetQuotaTotalInBytes(ulong* value)
{
return ((delegate* unmanaged[Stdcall]<IStorageProviderQuotaUI*, ulong*, HRESULT>)(lpVtbl[6]))((IStorageProviderQuotaUI*)Unsafe.AsPointer(ref this), value);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HRESULT GetQuotaUsedInBytes(ulong* value)
{
return ((delegate* unmanaged[Stdcall]<IStorageProviderQuotaUI*, ulong*, HRESULT>)(lpVtbl[8]))((IStorageProviderQuotaUI*)Unsafe.AsPointer(ref this), value);
}

public static ref readonly Guid Guid
{
get
{
// BA6295C3-312E-544F-9FD5-1F81B21F3649
ReadOnlySpan<byte> data =
[
0xC3, 0x95, 0x62, 0xBA,
0x2E, 0x31,
0x4F, 0x54,
0x9F, 0xD5,
0x1F, 0x81, 0xB2, 0x1F, 0x36, 0x49
];

Debug.Assert(data.Length == sizeof(Guid));
return ref Unsafe.As<byte, Guid>(ref MemoryMarshal.GetReference(data));
}
}
}
}
41 changes: 41 additions & 0 deletions src/Files.App.CsWin32/IStorageProviderStatusUI.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Win32.Foundation;

namespace Windows.Win32.System.WinRT
{
public unsafe struct IStorageProviderStatusUI : IComIID
{
private void** lpVtbl;

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderStatusUI.cs

View workflow job for this annotation

GitHub Actions / build (Release, arm64)

Field 'IStorageProviderStatusUI.lpVtbl' is never assigned to, and will always have its default value

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderStatusUI.cs

View workflow job for this annotation

GitHub Actions / build (Debug, arm64)

Field 'IStorageProviderStatusUI.lpVtbl' is never assigned to, and will always have its default value

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderStatusUI.cs

View workflow job for this annotation

GitHub Actions / build (Debug, x64)

Field 'IStorageProviderStatusUI.lpVtbl' is never assigned to, and will always have its default value

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderStatusUI.cs

View workflow job for this annotation

GitHub Actions / build (Release, x64)

Field 'IStorageProviderStatusUI.lpVtbl' is never assigned to, and will always have its default value

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HRESULT GetQuotaUI(IStorageProviderQuotaUI** result)
{
return ((delegate* unmanaged[Stdcall]<IStorageProviderStatusUI*, IStorageProviderQuotaUI**, HRESULT>)lpVtbl[14])((IStorageProviderStatusUI*)Unsafe.AsPointer(ref this), result);
}

public static ref readonly Guid Guid
{
get
{
// d6b6a758-198d-5b80-977f-5ff73da33118
ReadOnlySpan<byte> data =
[
0x58, 0xa7, 0xb6, 0xd6,
0x8d, 0x19,
0x80, 0x5b,
0x97, 0x7f,
0x5f, 0xf7, 0x3d, 0xa3, 0x31, 0x18
];

Debug.Assert(data.Length == sizeof(Guid));
return ref Unsafe.As<byte, Guid>(ref MemoryMarshal.GetReference(data));
}
}
}
}
41 changes: 41 additions & 0 deletions src/Files.App.CsWin32/IStorageProviderStatusUISource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Win32.Foundation;

namespace Windows.Win32.System.WinRT
{
public unsafe struct IStorageProviderStatusUISource : IComIID
{
private void** lpVtbl;

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderStatusUISource.cs

View workflow job for this annotation

GitHub Actions / build (Release, arm64)

Field 'IStorageProviderStatusUISource.lpVtbl' is never assigned to, and will always have its default value

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderStatusUISource.cs

View workflow job for this annotation

GitHub Actions / build (Debug, arm64)

Field 'IStorageProviderStatusUISource.lpVtbl' is never assigned to, and will always have its default value

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderStatusUISource.cs

View workflow job for this annotation

GitHub Actions / build (Debug, x64)

Field 'IStorageProviderStatusUISource.lpVtbl' is never assigned to, and will always have its default value

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderStatusUISource.cs

View workflow job for this annotation

GitHub Actions / build (Release, x64)

Field 'IStorageProviderStatusUISource.lpVtbl' is never assigned to, and will always have its default value

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HRESULT GetStatusUI(IStorageProviderStatusUI** result)
{
return ((delegate* unmanaged[Stdcall]<IStorageProviderStatusUISource*, IStorageProviderStatusUI**, HRESULT>)lpVtbl[6])((IStorageProviderStatusUISource*)Unsafe.AsPointer(ref this), result);
}

public static ref readonly Guid Guid
{
get
{
// A306C249-3D66-5E70-9007-E43DF96051FF
ReadOnlySpan<byte> data =
[
0x49, 0xc2, 0x06, 0xa3,
0x66, 0x3d,
0x70, 0x5e,
0x90, 0x07,
0xe4, 0x3d, 0xf9, 0x60, 0x51, 0xff
];

Debug.Assert(data.Length == sizeof(Guid));
return ref Unsafe.As<byte, Guid>(ref MemoryMarshal.GetReference(data));
}
}
}
}
41 changes: 41 additions & 0 deletions src/Files.App.CsWin32/IStorageProviderStatusUISourceFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Win32.Foundation;

namespace Windows.Win32.System.WinRT
{
public unsafe struct IStorageProviderStatusUISourceFactory : IComIID
{
private void** lpVtbl;

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderStatusUISourceFactory.cs

View workflow job for this annotation

GitHub Actions / build (Release, arm64)

Field 'IStorageProviderStatusUISourceFactory.lpVtbl' is never assigned to, and will always have its default value

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderStatusUISourceFactory.cs

View workflow job for this annotation

GitHub Actions / build (Debug, arm64)

Field 'IStorageProviderStatusUISourceFactory.lpVtbl' is never assigned to, and will always have its default value

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderStatusUISourceFactory.cs

View workflow job for this annotation

GitHub Actions / build (Debug, x64)

Field 'IStorageProviderStatusUISourceFactory.lpVtbl' is never assigned to, and will always have its default value

Check warning on line 14 in src/Files.App.CsWin32/IStorageProviderStatusUISourceFactory.cs

View workflow job for this annotation

GitHub Actions / build (Release, x64)

Field 'IStorageProviderStatusUISourceFactory.lpVtbl' is never assigned to, and will always have its default value

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HRESULT GetStatusUISource(nint syncRootId, IStorageProviderStatusUISource** result)
{
return ((delegate* unmanaged[Stdcall]<IStorageProviderStatusUISourceFactory*, nint, IStorageProviderStatusUISource**, HRESULT>)lpVtbl[6])((IStorageProviderStatusUISourceFactory*)Unsafe.AsPointer(ref this), syncRootId, result);
}

public static ref readonly Guid Guid
{
get
{
// 12E46B74-4E5A-58D1-A62F-0376E8EE7DD8
ReadOnlySpan<byte> data =
[
0x74, 0x6b, 0xe4, 0x12,
0x5a, 0x4e,
0xd1, 0x58,
0xa6, 0x2f,
0x03, 0x76, 0xe8, 0xee, 0x7d, 0xd8
];

Debug.Assert(data.Length == sizeof(Guid));
return ref Unsafe.As<byte, Guid>(ref MemoryMarshal.GetReference(data));
}
}
}
}
150 changes: 34 additions & 116 deletions src/Files.App/Utils/Storage/Helpers/SyncRootHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,144 +1,62 @@
using Microsoft.Win32;
// Copyright (c) Files Community
// Licensed under the MIT License.

using Microsoft.Win32;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Windows.Storage.Provider;
using Windows.Win32;
using Windows.Win32.Foundation;
using Windows.Win32.System.Com;
using Windows.Win32.System.WinRT;
using WinRT;

namespace Files.App.Utils.Storage
{
internal static class SyncRootHelpers
{
private unsafe struct IStorageProviderStatusUISourceFactory : IComIID
{
private void** vtbl;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public HRESULT GetStatusUISource(nint syncRootId, IStorageProviderStatusUISource** result)
{
return ((delegate* unmanaged[Stdcall]<IStorageProviderStatusUISourceFactory*, nint, IStorageProviderStatusUISource**, HRESULT>)vtbl[6])((IStorageProviderStatusUISourceFactory*)Unsafe.AsPointer(ref this), syncRootId, result);
}

public static ref readonly Guid Guid
{
get
{
// 12e46b74-4e5a-58d1-a62f-0376e8ee7dd8
ReadOnlySpan<byte> data = new byte[]
{
0x74, 0x6b, 0xe4, 0x12,
0x5a, 0x4e,
0xd1, 0x58,
0xa6, 0x2f,
0x03, 0x76, 0xe8, 0xee, 0x7d, 0xd8
};
Debug.Assert(data.Length == sizeof(Guid));
return ref Unsafe.As<byte, Guid>(ref MemoryMarshal.GetReference(data));
}
}
}

private unsafe struct IStorageProviderStatusUISource : IComIID
{
private void** vtbl;

public HRESULT GetStatusUI(IStorageProviderStatusUI** result)
{
return ((delegate* unmanaged[Stdcall]<IStorageProviderStatusUISource*, IStorageProviderStatusUI**, HRESULT>)vtbl[6])((IStorageProviderStatusUISource*)Unsafe.AsPointer(ref this), result);
}

public static ref readonly Guid Guid
{
get
{
// a306c249-3d66-5e70-9007-e43df96051ff
ReadOnlySpan<byte> data = new byte[]
{
0x49, 0xc2, 0x06, 0xa3,
0x66, 0x3d,
0x70, 0x5e,
0x90, 0x07,
0xe4, 0x3d, 0xf9, 0x60, 0x51, 0xff
};
Debug.Assert(data.Length == sizeof(Guid));
return ref Unsafe.As<byte, Guid>(ref MemoryMarshal.GetReference(data));
}
}
}

private unsafe struct IStorageProviderStatusUI : IComIID
{
public static ref readonly Guid Guid
{
get
{
// d6b6a758-198d-5b80-977f-5ff73da33118
ReadOnlySpan<byte> data = new byte[]
{
0x58, 0xa7, 0xb6, 0xd6,
0x8d, 0x19,
0x80, 0x5b,
0x97, 0x7f,
0x5f, 0xf7, 0x3d, 0xa3, 0x31, 0x18
};
Debug.Assert(data.Length == sizeof(Guid));
return ref Unsafe.As<byte, Guid>(ref MemoryMarshal.GetReference(data));
}
}
}

private static unsafe (bool Success, ulong Capacity, ulong Used) GetSyncRootQuotaFromSyncRootId(string syncRootId)
{
RegistryKey? key;
if ((key = Registry.LocalMachine.OpenSubKey($"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SyncRootManager\\{syncRootId}")) is null)
{
using var key = Registry.LocalMachine.OpenSubKey($"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\SyncRootManager\\{syncRootId}");
if (key?.GetValue("StorageProviderStatusUISourceFactory") is not string factoryClsidString ||
!Guid.TryParse(factoryClsidString, out var factoryClsid))
return (false, 0, 0);
}

using (key)
{
if (key.GetValue("StorageProviderStatusUISourceFactory") is string statusUIclass)
{
StorageProviderStatusUI statusUI;
using (ComPtr<IStorageProviderStatusUISourceFactory> sourceFactoryNative = default)
{
Guid statusUIclassGuid = Guid.Parse(statusUIclass);
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)
{
return (false, 0, 0);
}
HRESULT hr = default;
ulong ulTotalSize = 0ul, ulUsedSize = 0ul;
using ComPtr<IStorageProviderStatusUISourceFactory> pStorageProviderStatusUISourceFactory = default;
using ComPtr<IStorageProviderStatusUISource> pStorageProviderStatusUISource = default;
using ComPtr<IStorageProviderStatusUI> pStorageProviderStatusUI = default;
using ComPtr<IStorageProviderQuotaUI> pStorageProviderQuotaUI = default;

MarshalString.Pinnable syncRootIdHstring = new(syncRootId);
fixed (char* ptr = syncRootIdHstring)
using (ComPtr<IStorageProviderStatusUISource> sourceNative = default)
{
ExceptionHelpers.ThrowExceptionForHR(sourceFactoryNative.Get()->GetStatusUISource(syncRootIdHstring.GetAbi(), sourceNative.GetAddressOf()));
if (PInvoke.CoCreateInstance(
&factoryClsid,
null,
CLSCTX.CLSCTX_LOCAL_SERVER,
(Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in IStorageProviderStatusUISourceFactory.Guid)),
(void**)pStorageProviderStatusUISourceFactory.GetAddressOf()).ThrowIfFailedOnDebug().Failed)
return (false, 0, 0);

using (ComPtr<IStorageProviderStatusUI> statusNative = default)
{
ExceptionHelpers.ThrowExceptionForHR(sourceNative.Get()->GetStatusUI(statusNative.GetAddressOf()));
statusUI = StorageProviderStatusUI.FromAbi((nint)statusNative.Get());
}
}
}
return (true, statusUI.QuotaUI.QuotaTotalInBytes, statusUI.QuotaUI.QuotaUsedInBytes);
}
else
{
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);
}
}

return (true, ulTotalSize, ulUsedSize);
}

public static async Task<(bool Success, ulong Capacity, ulong Used)> GetSyncRootQuotaAsync(string path)
{
Windows.Storage.StorageFolder folder = await Windows.Storage.StorageFolder.GetFolderFromPathAsync(path);
StorageProviderSyncRootInfo? syncRootInfo = null;
Windows.Storage.Provider.StorageProviderSyncRootInfo? syncRootInfo = null;

try
{
syncRootInfo = StorageProviderSyncRootManager.GetSyncRootInformationForFolder(folder);
syncRootInfo = Windows.Storage.Provider.StorageProviderSyncRootManager.GetSyncRootInformationForFolder(folder);
}
catch
{
Expand Down
Loading