Functions/GenXdev.FileSystem/Move-ToRecycleBin.cs

using System.Management.Automation;
using System.Runtime.InteropServices;

namespace GenXdev.FileSystem
{
    [System.ComponentModel.Description(@"
.SYNOPSIS
Moves files and directories to the Windows Recycle Bin safely.
.DESCRIPTION
* Safely moves files or directories to the recycle bin using the Windows
  Shell API, even if they are currently in use.
* The function uses the Shell.Application COM object to perform the
  operation, ensuring proper recycling behavior and undo capability.

.LICENSE
Copyright (C) 2026 René Vaessen / GenXdev

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

.EXAMPLE
```powershell
Move-ToRecycleBin -Path ""C:\temp\old-report.txt""
```

Move a single file to the recycle bin.
.EXAMPLE
```powershell
""file1.txt"",""file2.txt"" | recycle
```

Move multiple files to the recycle bin using pipeline and alias.
")]
    [Cmdlet(VerbsCommon.Move, "ToRecycleBin")]
    [OutputType(typeof(bool))]
    public class MoveToRecycleBinCommand : PSGenXdevCmdlet
    {
        /// <summary>
        /// One or more paths to files or directories that should be moved to the recycle
        /// bin. Accepts pipeline input and wildcards. The paths must exist and be
        /// accessible.
        /// </summary>
        [Parameter(
            Mandatory = true,
            Position = 0,
            ValueFromPipeline = true,
            ValueFromPipelineByPropertyName = true,
            HelpMessage = "Specify the path(s) to move to the recycle bin")]
        [ValidateNotNullOrEmpty]
        [Alias("FullName")]
        public string[] Path { get; set; }

        private bool success = true;
        private dynamic shellObj;

        /// <summary>
        /// Begin processing - initialize shell automation object for recycle bin operations
        /// </summary>
        protected override void BeginProcessing()
        {
            // initialize shell automation object for recycle bin operations
            shellObj = null;

            try
            {
                shellObj = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
                WriteVerbose("Created Shell.Application COM object for recycle operations");
            }
            catch (Exception ex)
            {
                WriteError(new ErrorRecord(ex, "ComObjectCreationFailed", ErrorCategory.ResourceUnavailable, null));
                success = false;
            }
        }

        /// <summary>
        /// Process record - main cmdlet logic for processing each path
        /// </summary>
        protected override void ProcessRecord()
        {
            if (shellObj == null)
            {
                return;
            }

            foreach (string itemPath in Path)
            {
                // convert relative or shorthand paths to full filesystem paths
                string fullPath = ExpandPath(itemPath);

                WriteVerbose($"Processing path: {fullPath}");

                try
                {
                    // check if the target path actually exists before attempting to recycle
                    if (File.Exists(fullPath) || Directory.Exists(fullPath))
                    {
                        // confirm the recycle operation with the user
                        if (ShouldProcess(fullPath, "Move to Recycle Bin"))
                        {
                            // split the path into directory and filename for shell operation
                            string dirName = System.IO.Path.GetDirectoryName(fullPath);
                            string fileName = System.IO.Path.GetFileName(fullPath);

                            // get shell folder object for the directory containing the item
                            dynamic folderObj = shellObj.Namespace(dirName);
                            dynamic fileObj = folderObj.ParseName(fileName);

                            // perform the recycle operation using shell verbs
                            fileObj.InvokeVerb("delete");
                            WriteVerbose($"Successfully moved to recycle bin: {fullPath}");
                        }
                    }
                    else
                    {
                        WriteWarning($"Path not found: {fullPath}");
                        success = false;
                    }
                }
                catch (Exception ex)
                {
                    WriteError(new ErrorRecord(ex, "RecycleOperationFailed", ErrorCategory.InvalidOperation, fullPath));
                    success = false;
                }
            }
        }

        /// <summary>
        /// End processing - cleanup the COM object and return success status
        /// </summary>
        protected override void EndProcessing()
        {
            // cleanup the COM object to prevent resource leaks
            if (shellObj != null)
            {
                try
                {
                    Marshal.ReleaseComObject(shellObj);
                    WriteVerbose("Released Shell.Application COM object");
                }
                catch (Exception ex)
                {
                    WriteWarning($"Failed to release COM object: {ex.Message}");
                }
            }

            WriteObject(success);
        }
    }
}