Skip to content

Commit 57caef3

Browse files
committed
Revert changes to the recycle bin service
1 parent d2c559a commit 57caef3

File tree

4 files changed

+59
-48
lines changed

4 files changed

+59
-48
lines changed

src/Files.App.CsWin32/Extras.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public static unsafe nint SetWindowLongPtr(HWND hWnd, WINDOW_LONG_PTR_INDEX nInd
3838
: _SetWindowLongPtr(hWnd, (int)nIndex, dwNewLong);
3939
}
4040

41-
[DllImport("User32", EntryPoint = "SHUpdateRecycleBinIcon")]
41+
[DllImport("shell32.dll", EntryPoint = "SHUpdateRecycleBinIcon", CharSet = CharSet.Unicode, SetLastError = true)]
4242
public static extern void SHUpdateRecycleBinIcon();
4343
}
4444

src/Files.App/Actions/FileSystem/EmptyRecycleBinAction.cs

+6-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33

44
using Microsoft.UI.Xaml.Controls;
5+
using Windows.Foundation.Metadata;
56

67
namespace Files.App.Actions
78
{
@@ -42,16 +43,18 @@ public async Task ExecuteAsync(object? parameter = null)
4243
Content = Strings.ConfirmEmptyBinDialogContent.GetLocalizedResource(),
4344
PrimaryButtonText = Strings.Yes.GetLocalizedResource(),
4445
SecondaryButtonText = Strings.Cancel.GetLocalizedResource(),
45-
DefaultButton = ContentDialogButton.Primary,
46-
XamlRoot = MainWindow.Instance.Content.XamlRoot
46+
DefaultButton = ContentDialogButton.Primary
4747
};
4848

49+
if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8))
50+
confirmationDialog.XamlRoot = MainWindow.Instance.Content.XamlRoot;
51+
4952
if (UserSettingsService.FoldersSettingsService.DeleteConfirmationPolicy is DeleteConfirmationPolicies.Never ||
5053
await confirmationDialog.TryShowAsync() is ContentDialogResult.Primary)
5154
{
5255
var banner = StatusCenterHelper.AddCard_EmptyRecycleBin(ReturnResult.InProgress);
5356

54-
bool result = await StorageTrashBinService.EmptyTrashBin();
57+
bool result = await Task.Run(StorageTrashBinService.EmptyTrashBin);
5558

5659
StatusCenterViewModel.RemoveItem(banner);
5760

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public interface IStorageTrashBinService
5656
/// Deletes files and folders in Recycle Bin permanently.
5757
/// </summary>
5858
/// <returns>True if succeeded; otherwise, false</returns>
59-
Task<bool> EmptyTrashBin();
59+
bool EmptyTrashBin();
6060

6161
/// <summary>
6262
/// Restores files and folders in Recycle Bin to original paths.

src/Files.App/Services/Storage/StorageTrashBinService.cs

+51-43
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// Copyright (c) Files Community
22
// Licensed under the MIT License.
33

4-
using Microsoft.Extensions.Logging;
54
using System.Runtime.InteropServices;
65
using Windows.Win32;
76
using Windows.Win32.Foundation;
7+
using Windows.Win32.System.Com;
88
using Windows.Win32.UI.Shell;
99

1010
namespace Files.App.Services
@@ -68,68 +68,76 @@ public async Task<bool> CanGoTrashBin(string? path)
6868
}
6969

7070
/// <inheritdoc/>
71-
public async Task<bool> EmptyTrashBin()
71+
public bool EmptyTrashBin()
7272
{
73-
return await Win32Helper.StartSTATask(async () =>
73+
// TODO: Use IFileOperation instead of its wrapper for the operation status to be reported.
74+
var fRes = PInvoke.SHEmptyRecycleBin(
75+
new(),
76+
string.Empty,
77+
0x00000001 | 0x00000002 /* SHERB_NOCONFIRMATION | SHERB_NOPROGRESSUI */)
78+
.Succeeded;
79+
80+
return fRes;
81+
}
82+
83+
/// <inheritdoc/>
84+
public async Task<bool> RestoreAllTrashesAsync()
85+
{
86+
return await Win32Helper.StartSTATask(() =>
7487
{
7588
try
7689
{
77-
HRESULT hr = default;
78-
IChildFolder recycleBinFolder = new WindowsFolder(PInvoke.FOLDERID_RecycleBinFolder);
79-
using var bulkOperations = new WindowsBulkOperations(new(MainWindow.Instance.WindowHandle), FILEOPERATION_FLAGS.FOF_NO_UI);
80-
81-
await foreach (WindowsStorable item in recycleBinFolder.GetItemsAsync())
82-
hr = bulkOperations.QueueDeleteOperation(item).ThrowIfFailedOnDebug();
83-
84-
// Perform the operations queued
85-
hr = bulkOperations.PerformAllOperations().ThrowIfFailedOnDebug();
86-
87-
// Update the RecycleBin folder icon
88-
PInvoke.SHUpdateRecycleBinIcon();
90+
RestoreAllTrashesInternal();
8991

9092
return true;
9193
}
92-
catch (COMException ex)
94+
catch
9395
{
94-
App.Logger.LogWarning(ex, ex.Message);
9596
return false;
9697
}
9798
});
9899
}
99100

100-
/// <inheritdoc/>
101-
public async Task<bool> RestoreAllTrashesAsync()
101+
private unsafe bool RestoreAllTrashesInternal()
102102
{
103-
return await Win32Helper.StartSTATask(async () =>
103+
// Get IShellItem for Recycle Bin folder
104+
using ComPtr<IShellItem> pRecycleBinFolderShellItem = default;
105+
HRESULT hr = PInvoke.SHGetKnownFolderItem(FOLDERID.FOLDERID_RecycleBinFolder, KNOWN_FOLDER_FLAG.KF_FLAG_DEFAULT, HANDLE.Null, IID.IID_IShellItem, (void**)pRecycleBinFolderShellItem.GetAddressOf());
106+
107+
// Get IEnumShellItems for Recycle Bin folder
108+
using ComPtr<IEnumShellItems> pEnumShellItems = default;
109+
hr = pRecycleBinFolderShellItem.Get()->BindToHandler(null, BHID.BHID_EnumItems, IID.IID_IEnumShellItems, (void**)pEnumShellItems.GetAddressOf());
110+
111+
// Initialize how to perform the operation
112+
using ComPtr<IFileOperation> pFileOperation = default;
113+
hr = PInvoke.CoCreateInstance(CLSID.CLSID_FileOperation, null, CLSCTX.CLSCTX_LOCAL_SERVER, IID.IID_IFileOperation, (void**)pFileOperation.GetAddressOf());
114+
hr = pFileOperation.Get()->SetOperationFlags(FILEOPERATION_FLAGS.FOF_NO_UI);
115+
hr = pFileOperation.Get()->SetOwnerWindow(new(MainWindow.Instance.WindowHandle));
116+
117+
using ComPtr<IShellItem> pShellItem = default;
118+
while (pEnumShellItems.Get()->Next(1, pShellItem.GetAddressOf()) == HRESULT.S_OK)
104119
{
105-
try
106-
{
107-
HRESULT hr = default;
108-
IChildFolder recycleBinFolder = new WindowsFolder(PInvoke.FOLDERID_RecycleBinFolder);
109-
using var bulkOperations = new WindowsBulkOperations(new(MainWindow.Instance.WindowHandle), FILEOPERATION_FLAGS.FOF_NO_UI);
120+
// Get the original path
121+
using ComPtr<IShellItem2> pShellItem2 = default;
122+
hr = pShellItem.Get()->QueryInterface(IID.IID_IShellItem2, (void**)pShellItem2.GetAddressOf());
123+
hr = PInvoke.PSGetPropertyKeyFromName("System.Recycle.DeletedFrom", out var originalPathPropertyKey);
124+
hr = pShellItem2.Get()->GetString(originalPathPropertyKey, out var szOriginalPath);
110125

111-
await foreach (WindowsStorable item in recycleBinFolder.GetItemsAsync())
112-
{
113-
item.GetPropertyValue("System.Recycle.DeletedFrom", out string originalLocationFolderPath);
126+
// Get IShellItem of the original path
127+
hr = PInvoke.SHCreateItemFromParsingName(szOriginalPath.ToString(), null, typeof(IShellItem).GUID, out var pOriginalPathShellItemPtr);
128+
var pOriginalPathShellItem = (IShellItem*)pOriginalPathShellItemPtr;
114129

115-
if (WindowsStorable.TryParse(originalLocationFolderPath) is WindowsFolder originalLocationFolder)
116-
hr = bulkOperations.QueueMoveOperation(item, originalLocationFolder, null).ThrowIfFailedOnDebug();
117-
}
130+
// Define the shell item to restore
131+
hr = pFileOperation.Get()->MoveItem(pShellItem.Get(), pOriginalPathShellItem, default(PCWSTR), null);
132+
}
118133

119-
// Perform the operations queued
120-
hr = bulkOperations.PerformAllOperations().ThrowIfFailedOnDebug();
134+
// Perform
135+
hr = pFileOperation.Get()->PerformOperations();
121136

122-
// Update the RecycleBin folder icon
123-
PInvoke.SHUpdateRecycleBinIcon();
137+
// Reset the icon
138+
PInvoke.SHUpdateRecycleBinIcon();
124139

125-
return true;
126-
}
127-
catch (COMException ex)
128-
{
129-
App.Logger.LogWarning(ex, ex.Message);
130-
return false;
131-
}
132-
});
140+
return true;
133141
}
134142
}
135143
}

0 commit comments

Comments
 (0)