8000 Misuse of `SHEmptyRecycleBin` in `Clear-RecycleBin` cmdlet · Issue #6743 · PowerShell/PowerShell · GitHub
[go: up one dir, main page]

Skip to content

Misuse of SHEmptyRecycleBin in Clear-RecycleBin cmdlet #6743

@GeeLaw

Description

@GeeLaw

Overview

Clear-RecycleBin produces spurious error records when used in Windows PowerShell. (Yes I know this repo is dedicated to PowerShell Core, but since it contains the code, and UserVoice isn't quite effective, I'll just put the analysis here.)

In a freshly launched PowerShell instance, running Clear-RecycleBin -Force will produce the following error (whether or not the recycle bin is empty does not matter at all):

Clear-RecycleBin : The system cannot find the path specified
At line:1 char:1
+ Clear-RecycleBin -Force
+ ~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (RecycleBin:String) [Clear-RecycleBin], Win32Exception
    + FullyQualifiedErrorId : FailedToClearRecycleBin,Microsoft.PowerShell.Commands.ClearRecycleBinCommand

However, subsequent invocations to the same command (with -Force) will not produce such error. Moreover, in a newly launched PowerShell instance, running Clear-RecycleBin (without -Force) and manually confirming the operation) will not write error.

Root Cause

Upon close investigation, the root cause starts on line 226 of ClearRecycleBinCommand.cs. Relevant excerpt:

uint result = NativeMethod.SHEmptyRecycleBin(IntPtr.Zero, drivePath,
    NativeMethod.RecycleFlags.SHERB_NOCONFIRMATION |
    NativeMethod.RecycleFlags.SHERB_NOPROGRESSUI |
    NativeMethod.RecycleFlags.SHERB_NOSOUND);

int lastError = Marshal.GetLastWin32Error();
progress.PercentComplete = 100;
progress.RecordType = ProgressRecordType.Completed;
WriteProgress(progress);
// 0 is for a successful operation
// 203 comes up when trying to empty an already emptied recyclebin
// 18 comes up when there are no more files in the given recyclebin
if (!(lastError == 0 || lastError == 203 || lastError == 18))
{
    Win32Exception exception = new Win32Exception(lastError);
    WriteError(new ErrorRecord(exception, "FailedToClearRecycleBin", ErrorCategory.InvalidOperation, "RecycleBin"));
}

The mistake here is to use GetLastWin32Error -- according to SHEmptyRecycleBin function (Windows) on MSDN, the function uses HRESULT to indicate error status. The documentation does not mention GetLastError, and it is true that the function does not call SetLastError on the caller thread before returning.

The fact that GetLastError returns 3 on the first invocation of Clear-RecycleBin -Force is purely accidental.

Playing around with SHEmptyRecycleBin indicates that it returns S_OK (0) for success (also documented), and E_UNEXPECTED (0x8000FFFF, catastrophic/unexpected failure) when the recycle bin is already empty. At this time, I would suggest remove error detection in the cmdlet, since SHEmptyRecycleBin doesn't know how to handle the case when the recycle bin is already empty at all -- we can't expect it to do any reasonable failure indication.

There could have been a time when SHEmptyRecycleBin uses SetLastError to indicate failure, and the code could have been written when the documentation wasn't updated.


I wrote a blog entry on this issue, and I found that the code is wrong in every way. If you're interested, read https://geelaw.blog/entries/clear-recyclebin-shemptyrecyclebin/

Mistakes made summarised into a list:

  • not declaring [DllImport(SetLastError = true)] and using Marshal.GetLastWin32Error to retrieve the possible error code set by SHEmptyRecycleBin, which won't work and will get the leftover of previous marshalled calls.
  • documentation for SHEmptyRecycleBin doesn't indicate that it will call SetLastError, and recommends that one use the return value to determine error status.
  • SHEmptyRecycleBin doesn't expect to deal with already emptied recycle bin.

I would recommend anyone using this cmdlet use Clear-RecycleBin -ErrorAction SilentlyContinue to prevent irrelevant ErrorRecord.

Metadata

Metadata

Assignees

Labels

Issue-Code Cleanupthe issue is for cleaning up the code with no impact on functionalityResolution-FixedThe issue is fixed.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

    0