Skip to content

Folder shortcuts could not be restored from Recycle Bin - #18785

Open
Lins-ju wants to merge 2 commits into
files-community:mainfrom
Lins-ju:fix/folder-shortcuts-can-not-be-restored
Open

Folder shortcuts could not be restored from Recycle Bin#18785
Lins-ju wants to merge 2 commits into
files-community:mainfrom
Lins-ju:fix/folder-shortcuts-can-not-be-restored

Conversation

@Lins-ju

@Lins-ju Lins-ju commented Jul 31, 2026

Copy link
Copy Markdown

Resolved / Related Issues

  • Folder shortcuts could not be restored at all.
  • When folders are thrown in the recycle bin, before refreshing the recycle bin page, it will show the originalPath as the current folder path the file is in.

What i've found
After investigating the cascading functions used in order to perform a "Restore" action in the Recycle Bin, certain problems were found:

  1. FromShellItem was supposed to return a BinStorageFolder instead of a ShortcutItem, hiding the originalPath.
  2. Vanara could not retrieve the "DeletedFrom" path that could be used for an originalPath.
  3. The filePath used for restoring a recycle bin file was pointing to the recycle bin itself.

Why did they happen?:

  1. FromShellItem had an inverted if statement. For the function to work properly, it needs to check a "$Recycle.Bin" presence in the path first than it checks if the type equals "ShellLinkItem".
  2. That's probably because Recycle Bin files are not like any other generic files. For Recycle Bin files, you have a "$RXX..." that has its names changed to avoid name conflicts in Recycle Bin and a "$IXX..." file containing the metadata (OriginalPath, DateDeleted, etc). I haven't find an easy way to do that properly.
  3. "GetShellFileItem" receives a folderItem. But after debugging and testing, the folderItem would not have a correct originalPath, instead the originalPath would point to the current file path which would be in the recycle bin.

Other problems found

What did i do exacly and why

  • Used a FileStream to read its bytes directly from this metadata file. It seems to be the most reliable way to get the originalPath.
  • It has checks to prevent non recycle bin files to be read.
  • It has fallbacks to prevent passing a null filePath to the ShellFileItem. It will use the filePath itself when the "GetOriginalPathFromRecycleBinFile" returns null.

Steps used to test these changes

  • Create a folder shortcut
  • Delete folder shortcut
  • Open recycle bin
  • Try to restore shortcut

@CLAassistant

CLAassistant commented Jul 31, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

using var reader = new BinaryReader(stream, Encoding.Unicode);

// From the 24th to the 28th, contains the number of characters for the originalPath.
reader.BaseStream.Seek(24, SeekOrigin.Begin);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not preferable. Read System.Recycle.DeletedFrom prop on the deleted item to get the path to the original folder.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted. I was in a hurry and that day and forgot to mention.
System.Recycle.DeletedFrom was my first choice to solve this problem. It didn't work and had some issues:

  • When you freshly delete a folder and file, the System.Recycle.DeletedFrom will return empty.
  • System.Recycle.DeletedFrom is always returning empty for a shortcut of any kind.
  • AFTER updating the trash bin folder (F5), Folders and Files will have their correct original path. Other shortcuts will keep the original path as the recycle bin even after F5.

Will investigate it further to try to pin point why.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it's broken for links.

image

I'll use some time to investigate this in the explorer's implementation too.

@Lins-ju Lins-ju Aug 3, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may have found a fix.
System.Recycle.DeletedFrom does not exist for Shortcuts. I haven't realized before but, the original path lies in the target path itself.
I thought this would not work because if you used the target path as the Original Path for links, it would change the Original Path as soon as you moved the File or Folder it's linking itself to (But after testing, it does not!).
Will use that and see if it works.
I'll update this soon.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's what I found:

For the "Original Location" column in the Details view
There looks like a hidden item ID in an item's PIDL. An internal function uncovers that hidden item ID then restores the original path. This can be legally called via IShellFolder::GetDetailsOfEx.

image

For the "Restore the selected items" command
There's a dedicated internal class for that command.

image

If we just wanna execute the restore operation, we should do the latter (while both of them can be done in Files legally).

Here's the definition.

[GeneratedComInterface(StringMarshalling = StringMarshalling.Utf16), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("5869092d-8af9-4a6c-ae84-1f03be2246cc")]
public partial interface IRecycleBinManager
{
	[PreserveSig] HRESULT Compact();
	[PreserveSig] HRESULT GetFileData(string path, out DELETEDITEM deletedItem);
	[PreserveSig] HRESULT GetItemCount(out ulong itemCount);
	[PreserveSig] HRESULT GetUsedSpace(out ulong usedSpace);
	[PreserveSig] HRESULT IsEmpty();
	[PreserveSig] HRESULT PurgeAll(IFileOperation fileOperation);
	[PreserveSig] HRESULT PurgeItems([MarshalAs(UnmanagedType.LPWStr)] string path, IFileOperation fileOperation);
	[PreserveSig] HRESULT SuspendUpdating(int suspend);
	[PreserveSig] HRESULT HandleMenuMsg2(uint message, nuint wParam, nint lParam, out nint result);
	[PreserveSig] HRESULT RestoreItems(IShellItemArray items, IFileOperation fileOperation);
	[PreserveSig] HRESULT RestoreItem(IShellItem item, IFileOperation fileOperation);
	[PreserveSig] HRESULT IsRecycled([MarshalAs(UnmanagedType.LPWStr)] string path, out int isRecycled);
	[PreserveSig] HRESULT EnumItems(uint flags, [MarshalAs(UnmanagedType.Interface)] out IEnumRecycleItems enumerator);
	[PreserveSig] HRESULT WillRecycle([MarshalAs(UnmanagedType.LPWStr)] string path);
	[PreserveSig] HRESULT DelayCompaction(int delay);
	[PreserveSig] HRESULT GetRecycleBinCount(out int count);
	[PreserveSig] HRESULT GetRecycleBinAt(int index, in Guid interfaceId, [MarshalAs(UnmanagedType.Interface)] out object result);
	[PreserveSig] HRESULT GetRecycleBin([MarshalAs(UnmanagedType.LPWStr)] string path, in Guid interfaceId, [MarshalAs(UnmanagedType.Interface)] out object result);
	[PreserveSig] HRESULT Refresh();
}

[Guid("4A04656D-52AA-49DE-8A09-0B178760E748")]
partial class RecycleBinManager {}

Unfortunately the codebase in this area is not good enough to implement this easily but hope you find it useful.

@yair100 yair100 added the changes requested Changes are needed for this pull request label Aug 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changes requested Changes are needed for this pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Folder shortcuts in the recycle bin can not be restored

4 participants