Skip to content

Commit 9b8c1f6

Browse files
authored
Code Quality: Introduced HomeFolder for Quick Access in Home (#17088)
1 parent 6bcec6d commit 9b8c1f6

File tree

11 files changed

+327
-153
lines changed

11 files changed

+327
-153
lines changed

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

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Windows.Win32;
77
using Windows.Win32.Foundation;
88
using Windows.Win32.System.Com;
9+
using Windows.Win32.System.WinRT;
910

1011
namespace Windows.Win32
1112
{
@@ -16,8 +17,13 @@ public unsafe struct ComPtr<T> : IDisposable where T : unmanaged, IComIID
1617
{
1718
private T* _ptr;
1819

19-
public bool IsNull
20-
=> _ptr == null;
20+
public readonly bool IsNull
21+
{
22+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
23+
get => _ptr is null;
24+
}
25+
26+
// Constructors
2127

2228
public ComPtr(T* ptr)
2329
{
@@ -27,6 +33,9 @@ public ComPtr(T* ptr)
2733
((IUnknown*)ptr)->AddRef();
2834
}
2935

36+
// Methods
37+
38+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3039
public void Attach(T* other)
3140
{
3241
if (_ptr is not null)
@@ -35,6 +44,14 @@ public void Attach(T* other)
3544
_ptr = other;
3645
}
3746

47+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
48+
public T* Detach()
49+
{
50+
T* ptr = _ptr;
51+
_ptr = null;
52+
return ptr;
53+
}
54+
3855
[MethodImpl(MethodImplOptions.AggressiveInlining)]
3956
public readonly T* Get()
4057
{
@@ -48,19 +65,50 @@ public void Attach(T* other)
4865
}
4966

5067
[MethodImpl(MethodImplOptions.AggressiveInlining)]
68+
[Obsolete("Use `HRESULT As<U>(U** other)` instead.")]
5169
public readonly ComPtr<U> As<U>() where U : unmanaged, IComIID
5270
{
5371
ComPtr<U> ptr = default;
5472
((IUnknown*)_ptr)->QueryInterface((Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in U.Guid)), (void**)ptr.GetAddressOf());
5573
return ptr;
5674
}
5775

76+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
77+
public readonly HRESULT As<U>(U** other) where U : unmanaged, IComIID
78+
{
79+
return ((IUnknown*)_ptr)->QueryInterface((Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in U.Guid)), (void**)other);
80+
}
81+
82+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
83+
public readonly HRESULT As<U>(Guid* riid, IUnknown** other) where U : unmanaged, IComIID
84+
{
85+
return ((IUnknown*)_ptr)->QueryInterface(riid, (void**)other);
86+
}
87+
5888
[MethodImpl(MethodImplOptions.AggressiveInlining)]
5989
public readonly HRESULT CoCreateInstance(Guid* rclsid, IUnknown* pUnkOuter = null, CLSCTX dwClsContext = CLSCTX.CLSCTX_LOCAL_SERVER)
6090
{
6191
return PInvoke.CoCreateInstance(rclsid, pUnkOuter, dwClsContext, (Guid*)Unsafe.AsPointer(ref Unsafe.AsRef(in T.Guid)), (void**)this.GetAddressOf());
6292
}
6393

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+
110+
// Disposer
111+
64112
[MethodImpl(MethodImplOptions.AggressiveInlining)]
65113
public void Dispose()
66114
{

src/Files.App.CsWin32/ManualGuid.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ public static Guid* IID_IStorageProviderStatusUISourceFactory
3838

3939
[GuidRVAGen.Guid("2E941141-7F97-4756-BA1D-9DECDE894A3D")]
4040
public static partial Guid* IID_IApplicationActivationManager { get; }
41+
42+
[GuidRVAGen.Guid("00021500-0000-0000-C000-000000000046")]
43+
public static partial Guid* IID_IQueryInfo { get; }
4144
}
4245

4346
public static unsafe partial class CLSID

src/Files.App.CsWin32/NativeMethods.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,3 +222,7 @@ SetCurrentProcessExplicitAppUserModelID
222222
GdipCreateBitmapFromScan0
223223
BITMAP
224224
GetObject
225+
_SICHINTF
226+
RoGetAgileReference
227+
IQueryInfo
228+
QITIPF_FLAGS

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
namespace Files.App.Storage
1010
{
11-
public abstract class WindowsStorable : IWindowsStorable, IStorableChild
11+
public abstract class WindowsStorable : IWindowsStorable, IStorableChild, IEquatable<IWindowsStorable>
1212
{
1313
public ComPtr<IShellItem> ThisPtr { get; protected set; }
1414

@@ -65,6 +65,17 @@ public abstract class WindowsStorable : IWindowsStorable, IStorableChild
6565
return Task.FromResult<IFolder?>(new WindowsFolder(pParentFolder));
6666
}
6767

68+
/// <inheritdoc/>
69+
public override bool Equals(object? obj)
70+
{
71+
return Equals(obj as IWindowsStorable);
72+
}
73+
74+
public override int GetHashCode()
75+
{
76+
return HashCode.Combine(Id, Name);
77+
}
78+
6879
/// <inheritdoc/>
6980
public void Dispose()
7081
{
@@ -76,5 +87,20 @@ public override string ToString()
7687
{
7788
return this.GetDisplayName();
7889
}
90+
91+
/// <inheritdoc/>
92+
public unsafe bool Equals(IWindowsStorable? other)
93+
{
94+
if (other is null)
95+
return false;
96+
97+
return ThisPtr.Get()->Compare(other.ThisPtr.Get(), (uint)_SICHINTF.SICHINT_DISPLAY, out int order).Succeeded && order is 0;
98+
}
99+
100+
public static bool operator ==(WindowsStorable left, WindowsStorable right)
101+
=> left.Equals(right);
102+
103+
public static bool operator !=(WindowsStorable left, WindowsStorable right)
104+
=> !(left == right);
79105
}
80106
}

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

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

4+
using System.Runtime.CompilerServices;
5+
using System.Text;
46
using Windows.Win32;
57
using Windows.Win32.Foundation;
68
using Windows.Win32.System.SystemServices;
79
using Windows.Win32.UI.Shell;
10+
using Windows.Win32.UI.Shell.PropertiesSystem;
11+
using Windows.Win32.UI.WindowsAndMessaging;
812

913
namespace Files.App.Storage
1014
{
@@ -13,14 +17,27 @@ public static partial class WindowsStorableHelpers
1317
public unsafe static HRESULT GetPropertyValue<TValue>(this IWindowsStorable storable, string propKey, out TValue value)
1418
{
1519
using ComPtr<IShellItem2> pShellItem2 = default;
16-
var shellItem2Iid = typeof(IShellItem2).GUID;
17-
HRESULT hr = storable.ThisPtr.Get()->QueryInterface(&shellItem2Iid, (void**)pShellItem2.GetAddressOf());
18-
hr = PInvoke.PSGetPropertyKeyFromName(propKey, out var originalPathPropertyKey);
19-
hr = pShellItem2.Get()->GetString(originalPathPropertyKey, out var szOriginalPath);
20+
HRESULT hr = storable.ThisPtr.Get()->QueryInterface(IID.IID_IShellItem2, (void**)pShellItem2.GetAddressOf());
21+
22+
PROPERTYKEY propertyKey = default;
23+
fixed (char* pszPropertyKey = propKey)
24+
hr = PInvoke.PSGetPropertyKeyFromName(pszPropertyKey, &propertyKey);
2025

2126
if (typeof(TValue) == typeof(string))
2227
{
23-
value = (TValue)(object)szOriginalPath.ToString();
28+
ComHeapPtr<PWSTR> szPropertyValue = default;
29+
hr = pShellItem2.Get()->GetString(&propertyKey, szPropertyValue.Get());
30+
value = (TValue)(object)szPropertyValue.Get()->ToString();
31+
32+
return hr;
33+
}
34+
if (typeof(TValue) == typeof(bool))
35+
{
36+
bool propertyValue = false;
37+
hr = pShellItem2.Get()->GetBool(propertyKey, out var fPropertyValue);
38+
propertyValue = fPropertyValue;
39+
value = Unsafe.As<bool, TValue>(ref propertyValue);
40+
2441
return hr;
2542
}
2643
else
@@ -51,5 +68,80 @@ public unsafe static string GetDisplayName(this IWindowsStorable storable, SIGDN
5168
? new string((char*)pszName.Get()) // this is safe as it gets memcpy'd internally
5269
: string.Empty;
5370
}
71+
72+
public unsafe static HRESULT TryInvokeContextMenuVerb(this IWindowsStorable storable, string verbName)
73+
{
74+
Debug.Assert(Thread.CurrentThread.GetApartmentState() is ApartmentState.STA);
75+
76+
using ComPtr<IContextMenu> pContextMenu = default;
77+
HRESULT hr = storable.ThisPtr.Get()->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf());
78+
HMENU hMenu = PInvoke.CreatePopupMenu();
79+
hr = pContextMenu.Get()->QueryContextMenu(hMenu, 0, 1, 0x7FFF, PInvoke.CMF_OPTIMIZEFORINVOKE);
80+
81+
CMINVOKECOMMANDINFO cmici = default;
82+
cmici.cbSize = (uint)sizeof(CMINVOKECOMMANDINFO);
83+
cmici.nShow = (int)SHOW_WINDOW_CMD.SW_HIDE;
84+
85+
fixed (byte* pszVerbName = Encoding.ASCII.GetBytes(verbName))
86+
{
87+
cmici.lpVerb = new(pszVerbName);
88+
hr = pContextMenu.Get()->InvokeCommand(cmici);
89+
90+
if (!PInvoke.DestroyMenu(hMenu))
91+
return HRESULT.E_FAIL;
92+
93+
return hr;
94+
}
95+
}
96+
97+
public unsafe static HRESULT TryInvokeContextMenuVerbs(this IWindowsStorable storable, string[] verbNames, bool earlyReturnOnSuccess)
98+
{
99+
Debug.Assert(Thread.CurrentThread.GetApartmentState() is ApartmentState.STA);
100+
101+
using ComPtr<IContextMenu> pContextMenu = default;
102+
HRESULT hr = storable.ThisPtr.Get()->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IContextMenu, (void**)pContextMenu.GetAddressOf());
103+
HMENU hMenu = PInvoke.CreatePopupMenu();
104+
hr = pContextMenu.Get()->QueryContextMenu(hMenu, 0, 1, 0x7FFF, PInvoke.CMF_OPTIMIZEFORINVOKE);
105+
106+
CMINVOKECOMMANDINFO cmici = default;
107+
cmici.cbSize = (uint)sizeof(CMINVOKECOMMANDINFO);
108+
cmici.nShow = (int)SHOW_WINDOW_CMD.SW_HIDE;
109+
110+
foreach (var verbName in verbNames)
111+
{
112+
fixed (byte* pszVerbName = Encoding.ASCII.GetBytes(verbName))
113+
{
114+
cmici.lpVerb = new(pszVerbName);
115+
hr = pContextMenu.Get()->InvokeCommand(cmici);
116+
117+
if (!PInvoke.DestroyMenu(hMenu))
118+
return HRESULT.E_FAIL;
119+
120+
if (hr.Succeeded && earlyReturnOnSuccess)
121+
return hr;
122+
}
123+
}
124+
125+
return hr;
126+
}
127+
128+
public unsafe static HRESULT TryGetShellTooltip(this IWindowsStorable storable, out string? tooltip)
129+
{
130+
tooltip = null;
131+
132+
using ComPtr<IQueryInfo> pQueryInfo = default;
133+
HRESULT hr = storable.ThisPtr.Get()->BindToHandler(null, BHID.BHID_SFUIObject, IID.IID_IQueryInfo, (void**)pQueryInfo.GetAddressOf());
134+
if (hr.ThrowIfFailedOnDebug().Failed)
135+
return hr;
136+
137+
pQueryInfo.Get()->GetInfoTip((uint)QITIPF_FLAGS.QITIPF_DEFAULT, out var pszTip);
138+
if (hr.ThrowIfFailedOnDebug().Failed)
139+
return hr;
140+
141+
tooltip = pszTip.ToString();
142+
PInvoke.CoTaskMemFree(pszTip);
143+
144+
return HRESULT.S_OK;
145+
}
54146
}
55147
}

src/Files.App/Data/Contexts/HomePage/HomePageContext.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public sealed partial class HomePageContext : ObservableObject, IHomePageContext
1313

1414
public bool IsAnyItemRightClicked => rightClickedItem is not null;
1515

16+
public IHomeFolder HomeFolder { get; } = new HomeFolder();
17+
1618
private WidgetCardItem? rightClickedItem = null;
1719
public WidgetCardItem? RightClickedItem => rightClickedItem;
1820

src/Files.App/Data/Contexts/HomePage/IHomePageContext.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,10 @@ public interface IHomePageContext
2626
/// Tells whether any item has been right clicked
2727
/// </summary>
2828
bool IsAnyItemRightClicked { get; }
29+
30+
/// <summary>
31+
/// Gets the instance of <see cref="IHomeFolder"/>.
32+
/// </summary>
33+
IHomeFolder HomeFolder { get; }
2934
}
3035
}

0 commit comments

Comments
 (0)