Skip to content

Commit 0d0a836

Browse files
committed
Update
1 parent 77afe0b commit 0d0a836

File tree

5 files changed

+75
-56
lines changed

5 files changed

+75
-56
lines changed

src/Files.App.CsWin32/NativeMethods.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,5 @@ SHARD
138138
BHID_EnumItems
139139
FOLDERID_RecycleBinFolder
140140
CoTaskMemFree
141+
SHGetIDListFromObject
142+
SHCreateItemFromIDList

src/Files.App/Data/Contracts/IWindowsRecentItemsService.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,5 @@ public interface IWindowsRecentItemsService
5454
/// Clears recent files and folders of File Explorer.
5555
/// </summary>
5656
bool Clear();
57-
58-
/// <summary>
59-
/// Checks multiple visibility settings of recent items that can be configured in Windows Settings or Group Policy, in Windows Registry.
60-
/// </summary>
61-
bool CheckIsRecentItemsEnabled();
6257
}
6358
}

src/Files.App/Data/Items/WidgetRecentItem.cs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,21 @@
22
// Licensed under the MIT License. See the LICENSE.
33

44
using Microsoft.UI.Xaml.Media.Imaging;
5+
using Windows.Win32;
6+
using Windows.Win32.UI.Shell.Common;
57

68
namespace Files.App.Data.Items
79
{
810
/// <summary>
911
/// Represents an item for recent item of File Explorer on Windows.
1012
/// </summary>
11-
public sealed class RecentItem : WidgetCardItem, IEquatable<RecentItem>
13+
public sealed class RecentItem : WidgetCardItem, IEquatable<RecentItem>, IDisposable
1214
{
13-
private BitmapImage _Icon;
15+
private BitmapImage? _Icon;
1416
/// <summary>
1517
/// Gets or sets thumbnail icon of the recent item.
1618
/// </summary>
17-
public BitmapImage Icon
19+
public BitmapImage? Icon
1820
{
1921
get => _Icon;
2022
set => SetProperty(ref _Icon, value);
@@ -23,12 +25,20 @@ public BitmapImage Icon
2325
/// <summary>
2426
/// Gets or sets name of the recent item.
2527
/// </summary>
26-
public string Name { get; set; }
28+
public required string Name { get; set; }
2729

2830
/// <summary>
2931
/// Gets or sets target path of the recent item.
3032
/// </summary>
31-
public override string Path { get; set; }
33+
public override required string Path { get; set; }
34+
35+
/// <summary>
36+
/// Gets or initializes PIDL of the recent item.
37+
/// </summary>
38+
/// <remarks>
39+
/// This has to be removed in the future.
40+
/// </remarks>
41+
public unsafe required ITEMIDLIST* PIDL { get; init; }
3242

3343
/// <summary>
3444
/// Loads thumbnail icon of the recent item.
@@ -45,6 +55,11 @@ public async Task LoadRecentItemIconAsync()
4555

4656
public override int GetHashCode() => (Path, Name).GetHashCode();
4757
public override bool Equals(object? other) => other is RecentItem item && Equals(item);
48-
public bool Equals(RecentItem other) => other.Name == Name && other.Path == Path;
58+
public bool Equals(RecentItem? other) => other is not null && other.Name == Name && other.Path == Path;
59+
60+
public unsafe void Dispose()
61+
{
62+
PInvoke.CoTaskMemFree(PIDL);
63+
}
4964
}
5065
}

src/Files.App/Services/Windows/WindowsRecentItemsService.cs

Lines changed: 33 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
// Licensed under the MIT License. See the LICENSE.
33

44
using Microsoft.Extensions.Logging;
5-
using Microsoft.Win32;
65
using System.Collections.Specialized;
76
using Windows.Win32;
87
using Windows.Win32.Foundation;
8+
using Windows.Win32.System.Com;
99
using Windows.Win32.UI.Shell;
1010

1111
namespace Files.App.Services
@@ -108,60 +108,48 @@ public unsafe bool Add(string path)
108108

109109
/// <inheritdoc/>
110110
public unsafe bool Remove(RecentItem item)
111-
{
112-
return false; // TODO: Use ContextMenu class to invoke "delete" verb
113-
}
114-
115-
/// <inheritdoc/>
116-
public unsafe bool Clear()
117111
{
118112
try
119113
{
120-
PInvoke.SHAddToRecentDocs((uint)SHARD.SHARD_PIDL, null);
114+
// Initialize IFileOperation instance
115+
using ComPtr<IFileOperation> pFileOperation = default;
116+
var fileOperationIid = typeof(IFileOperation).GUID;
117+
var fileOperationInstanceIid = typeof(FileOperation).GUID;
118+
HRESULT hr = PInvoke.CoCreateInstance(&fileOperationInstanceIid, null, CLSCTX.CLSCTX_LOCAL_SERVER, &fileOperationIid, (void**)pFileOperation.GetAddressOf());
119+
hr = pFileOperation.Get()->SetOperationFlags(FILEOPERATION_FLAGS.FOF_NO_UI);
120+
hr = pFileOperation.Get()->SetOwnerWindow(new(MainWindow.Instance.WindowHandle));
121+
122+
// Get IShellItem from PIDL
123+
var shellItemIid = typeof(IShellItem).GUID;
124+
ComPtr<IShellItem> pShellItem = default;
125+
PInvoke.SHCreateItemFromIDList(item.PIDL, &shellItemIid, (void**)pShellItem.GetAddressOf());
126+
127+
// Perform deletion
128+
hr = pFileOperation.Get()->DeleteItem(pShellItem.Get(), null);
129+
hr = pFileOperation.Get()->PerformOperations();
121130

122131
return true;
123132
}
124-
catch (Exception ex)
133+
catch
125134
{
126-
App.Logger.LogWarning(ex, ex.Message);
127135
return false;
128136
}
129137
}
130138

131139
/// <inheritdoc/>
132-
public bool CheckIsRecentItemsEnabled()
140+
public unsafe bool Clear()
133141
{
134-
using var explorerSubKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer");
135-
using var advSubkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced");
136-
using var userPolicySubkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer");
137-
using var sysPolicySubkey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer");
138-
139-
if (explorerSubKey is not null)
142+
try
140143
{
141-
// File Explorer settings (default: 1)
142-
bool showRecentValue = Convert.ToBoolean(explorerSubKey.GetValue("ShowRecent", true));
143-
if (!showRecentValue)
144-
return false;
145-
}
144+
PInvoke.SHAddToRecentDocs((uint)SHARD.SHARD_PIDL, null);
146145

147-
if (advSubkey is not null)
148-
{
149-
// Windows Settings (default: 1)
150-
bool startTrackDocsValue = Convert.ToBoolean(advSubkey.GetValue("Start_TrackDocs", true));
151-
if (!startTrackDocsValue)
152-
return false;
146+
return true;
153147
}
154-
155-
// Group Policy settings (default: 0)
156-
var policySubkey = userPolicySubkey ?? sysPolicySubkey;
157-
if (policySubkey is not null)
148+
catch (Exception ex)
158149
{
159-
bool noRecentDocsHistoryValue = Convert.ToBoolean(policySubkey.GetValue("NoRecentDocsHistory", false));
160-
if (noRecentDocsHistoryValue)
161-
return false;
150+
App.Logger.LogWarning(ex, ex.Message);
151+
return false;
162152
}
163-
164-
return true;
165153
}
166154

167155
private unsafe bool UpdateRecentItems(bool isFolder)
@@ -172,8 +160,8 @@ private unsafe bool UpdateRecentItems(bool isFolder)
172160

173161
string szFolderShellPath =
174162
isFolder
175-
? "Shell:::{22877a6d-37a1-461a-91b0-dbda5aaebc99}" // Recent Places folder
176-
: "Shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}"; // Quick Access folder
163+
? "Shell:::{22877A6D-37A1-461A-91B0-DBDA5AAEBC99}" // Recent Places folder (recent folders)
164+
: "Shell:::{679F85CB-0220-4080-B29B-5540CC05AAB6}"; // Quick Access folder (recent files)
177165

178166
// Get IShellItem of the shell folder
179167
var shellItemIid = typeof(IShellItem).GUID;
@@ -200,12 +188,12 @@ private unsafe bool UpdateRecentItems(bool isFolder)
200188
// Get the target path
201189
pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_DESKTOPABSOLUTEEDITING, out var szDisplayName);
202190
var targetPath = szDisplayName.ToString();
203-
PInvoke.CoTaskMemFree((void*)szDisplayName.Value);
191+
PInvoke.CoTaskMemFree(szDisplayName.Value);
204192

205193
// Get the display name
206194
pShellItem.Get()->GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY, out szDisplayName);
207195
var fileName = szDisplayName.ToString();
208-
PInvoke.CoTaskMemFree((void*)szDisplayName.Value);
196+
PInvoke.CoTaskMemFree(szDisplayName.Value);
209197

210198
// Strip the file extension except when the file name only contains extension (e.g. ".gitignore")
211199
if (!FoldersSettingsService.ShowFileExtensions)
@@ -214,12 +202,13 @@ private unsafe bool UpdateRecentItems(bool isFolder)
214202
fileName = string.IsNullOrEmpty(strippedExtension) ? SystemIO.Path.GetFileName(fileName) : strippedExtension;
215203
}
216204

217-
// TODO: Get PIDL to prepare for removal of the item via "delete" verb for now
205+
PInvoke.SHGetIDListFromObject((IUnknown*)pShellItem.Get(), out var pidl);
218206

219207
recentItems.Add(new()
220208
{
221209
Path = targetPath,
222210
Name = fileName,
211+
PIDL = pidl,
223212
});
224213

225214
index++;
@@ -238,15 +227,15 @@ private unsafe bool UpdateRecentItems(bool isFolder)
238227
{
239228
_RecentFolders.Clear();
240229
_RecentFolders.AddRange(recentItems);
241-
}
230+
}
242231
}
243232
else
244233
{
245234
lock (_RecentFiles)
246235
{
247236
_RecentFiles.Clear();
248237
_RecentFiles.AddRange(recentItems);
249-
}
238+
}
250239
}
251240

252241
var eventArgs = GetChangedActionEventArgs(snapshot, recentItems);

src/Files.App/ViewModels/UserControls/Widgets/RecentFilesWidgetViewModel.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using Microsoft.Extensions.Logging;
55
using Microsoft.UI.Xaml.Controls;
6+
using Microsoft.Win32;
67
using System.Collections.Specialized;
78
using System.IO;
89
using Windows.Foundation.Metadata;
@@ -66,10 +67,11 @@ public RecentFilesWidgetViewModel()
6667

6768
public async Task RefreshWidgetAsync()
6869
{
69-
IsRecentFilesDisabledInWindows = !WindowsRecentItemsService.CheckIsRecentItemsEnabled();
70+
IsRecentFilesDisabledInWindows = !CheckIsRecentItemsEnabled();
7071
await WindowsRecentItemsService.UpdateRecentFilesAsync();
7172
}
7273

74+
7375
public override List<ContextMenuFlyoutItemViewModel> GetItemMenuItems(WidgetCardItem item, bool isPinned, bool isFolder = false)
7476
{
7577
return new List<ContextMenuFlyoutItemViewModel>()
@@ -232,6 +234,22 @@ public void NavigateToPath(string path)
232234
catch (Exception) { }
233235
}
234236

237+
public bool CheckIsRecentItemsEnabled()
238+
{
239+
using var explorerSubKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer");
240+
using var advSubkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced");
241+
using var userPolicySubkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer");
242+
using var sysPolicySubkey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer");
243+
var policySubkey = userPolicySubkey ?? sysPolicySubkey;
244+
245+
if (Convert.ToBoolean(explorerSubKey?.GetValue("ShowRecent", true)) &&
246+
Convert.ToBoolean(advSubkey?.GetValue("Start_TrackDocs", true)) &&
247+
!Convert.ToBoolean(policySubkey?.GetValue("NoRecentDocsHistory", false)))
248+
return true;
249+
250+
return false;
251+
}
252+
235253
// Event methods
236254

237255
private async void Manager_RecentFilesChanged(object? sender, NotifyCollectionChangedEventArgs e)

0 commit comments

Comments
 (0)