将文件发送到回收站

103

目前我正在使用以下函数

file.Delete();

但是我如何使用这个函数将文件发送到回收站而不是直接彻底删除它?


10
http://msdn.microsoft.com/en-us/library/ms127976.aspx - Jaroslav Jandek
3
@UweKeim的链接现在已经失效了,您可以在这里找到MSDN杂志(2007年12月)的.chm格式版本,文章名为“ .NET Matters: IFileOperation in Windows Vista”,它位于“Columns”文件夹中。 - jrh
这篇文章在我的.chm文件中无法打开。这个链接可以使用:https://learn.microsoft.com/en-us/archive/msdn-magazine/2007/december/net-matters-ifileoperation-in-windows-vista - RandomEngy
此外,您需要将 FOFX_RECYCLEONDELETE = 0x00080000 添加到操作标志中,该标志仅在 Windows 8 或更高版本上受支持。 - RandomEngy
8个回答

178

使用FileSystem.DeleteFile,并指定正确的RecycleOption。虽然这对交互式UI应用程序有效,但对于非UI交互式应用程序(如Windows服务应用程序)无效。


23
这是一个完全合适的解决方案,不应该被投下反对票。我想知道为什么访问VisualBasic库是“丑陋”的,请给出参考资料。 - jsmith
7
尤其是在这种情况下,“Microsoft.VisualBasic.FileIO.FileSystem”基本上与在此处使用“SHFileOperation”的示例相同。 - Dirk Vollmar
23
"丑陋,是吗?对我来说,WinAPI的方式更加丑陋 - 而且你很容易把某些东西搞砸。我个人不喜欢VB的语法,但在汇编中它只是 IL ,所以我不介意。顺便说一句,VB汇编调用的是同一个WinAPI函数。" - Jaroslav Jandek
7
过时了吗?您是否误将汇编与Microsoft.VisualBasic.Compatibility混淆了?我会避免使用后者,但似乎它不会很快被弃用(因为它在RDL报表引擎等方面被使用)。 - Jaroslav Jandek
7
使用内置的框架组件似乎比直接映射到shell32.dll更好。使用框架组件可以使变化更可移植并获得后续演进。而如果映射到系统库,你就有可能很快过时。 - fredlegrain
显示剩余16条评论

74

注意:这也不适用于非UI交互应用程序,如Windows服务。

这个包装器可以为您提供所需的功能:

using System.Runtime.InteropServices;

public class FileOperationAPIWrapper
    {
        /// <summary>
        /// Possible flags for the SHFileOperation method.
        /// </summary>
        [Flags]
        public enum FileOperationFlags : ushort
        {
            /// <summary>
            /// Do not show a dialog during the process
            /// </summary>
            FOF_SILENT = 0x0004,
            /// <summary>
            /// Do not ask the user to confirm selection
            /// </summary>
            FOF_NOCONFIRMATION = 0x0010,
            /// <summary>
            /// Delete the file to the recycle bin.  (Required flag to send a file to the bin
            /// </summary>
            FOF_ALLOWUNDO = 0x0040,
            /// <summary>
            /// Do not show the names of the files or folders that are being recycled.
            /// </summary>
            FOF_SIMPLEPROGRESS = 0x0100,
            /// <summary>
            /// Surpress errors, if any occur during the process.
            /// </summary>
            FOF_NOERRORUI = 0x0400,
            /// <summary>
            /// Warn if files are too big to fit in the recycle bin and will need
            /// to be deleted completely.
            /// </summary>
            FOF_WANTNUKEWARNING = 0x4000,
        }

        /// <summary>
        /// File Operation Function Type for SHFileOperation
        /// </summary>
        public enum FileOperationType : uint
        {
            /// <summary>
            /// Move the objects
            /// </summary>
            FO_MOVE = 0x0001,
            /// <summary>
            /// Copy the objects
            /// </summary>
            FO_COPY = 0x0002,
            /// <summary>
            /// Delete (or recycle) the objects
            /// </summary>
            FO_DELETE = 0x0003,
            /// <summary>
            /// Rename the object(s)
            /// </summary>
            FO_RENAME = 0x0004,
        }



        /// <summary>
        /// SHFILEOPSTRUCT for SHFileOperation from COM
        /// </summary>
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        private struct SHFILEOPSTRUCT
        {

            public IntPtr hwnd;
            [MarshalAs(UnmanagedType.U4)]
            public FileOperationType wFunc;
            public string pFrom;
            public string pTo;
            public FileOperationFlags fFlags;
            [MarshalAs(UnmanagedType.Bool)]
            public bool fAnyOperationsAborted;
            public IntPtr hNameMappings;
            public string lpszProgressTitle;
        }

        [DllImport("shell32.dll", CharSet = CharSet.Auto)]
        private static extern int SHFileOperation(ref SHFILEOPSTRUCT FileOp);

        /// <summary>
        /// Send file to recycle bin
        /// </summary>
        /// <param name="path">Location of directory or file to recycle</param>
        /// <param name="flags">FileOperationFlags to add in addition to FOF_ALLOWUNDO</param>
        public static bool Send(string path, FileOperationFlags flags)
        {
            try
            {
                var fs = new SHFILEOPSTRUCT
                                        {
                                            wFunc = FileOperationType.FO_DELETE,
                                            pFrom = path + '\0' + '\0',
                                            fFlags = FileOperationFlags.FOF_ALLOWUNDO | flags
                                        };
                SHFileOperation(ref fs);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// Send file to recycle bin.  Display dialog, display warning if files are too big to fit (FOF_WANTNUKEWARNING)
        /// </summary>
        /// <param name="path">Location of directory or file to recycle</param>
        public static bool Send(string path)
        {
            return Send(path, FileOperationFlags.FOF_NOCONFIRMATION | FileOperationFlags.FOF_WANTNUKEWARNING);
        }

        /// <summary>
        /// Send file silently to recycle bin.  Surpress dialog, surpress errors, delete if too large.
        /// </summary>
        /// <param name="path">Location of directory or file to recycle</param>
        public static bool MoveToRecycleBin(string path)
        {
            return Send(path, FileOperationFlags.FOF_NOCONFIRMATION | FileOperationFlags.FOF_NOERRORUI | FileOperationFlags.FOF_SILENT);

        }

        private static bool deleteFile(string path, FileOperationFlags flags)
        {
            try
            {
                var fs = new SHFILEOPSTRUCT
                                        {
                                            wFunc = FileOperationType.FO_DELETE,
                                            pFrom = path + '\0' + '\0',
                                            fFlags = flags
                                        };
                SHFileOperation(ref fs);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public static bool DeleteCompletelySilent(string path)
        {
            return deleteFile(path,
                              FileOperationFlags.FOF_NOCONFIRMATION | FileOperationFlags.FOF_NOERRORUI |
                              FileOperationFlags.FOF_SILENT);
        }
    }

5
如果在编译64位平台时要移除Pack = 1(否则会失败)。如果没有指定Pack = 1,它将适用于32位和64位。详见http://pinvoke.net/default.aspx/Structures/SHFILEOPSTRUCT.html。 - Sean
1
使用Pack = 1时,会抛出AccessViolationException异常。将其移除即可解决问题。顺便提一下,这是在64位Windows上的情况。 - P1nGu1n
2
运行此代码需要什么要求?我删除了Pack = 1,但它仍无法编译。DllImport、DllImportAttribute、MarshalAs、MarshalAsAttribute、StructLayout、StructLayoutAttribute不存在作为命名空间。请帮忙,谢谢 :) - puretppc
1
请注意:此功能不适用于非UI交互式应用程序,如Windows服务,仅适用于UI交互式应用程序。 - rboy
2
SHFileOperation 不支持长路径,即使使用 \?\ 前缀,对于超过 MAX_PATH 的路径也会失败。 - Melvyn
显示剩余7条评论

45

来自MSDN:

添加对Microsoft.VisualBasic程序集的引用。这个库中包含了所需的类。

在文件顶部添加此使用语句using Microsoft.VisualBasic.FileIO;

使用FileSystem.DeleteFile删除一个文件,它有选项来指定是否将其发送到回收站。

使用FileSystem.DeleteDirectory删除目录,并可选地指定将其发送到回收站。


包含Microsoft.VisualBasic的问题在于它与我程序中其他地方(GetFiles()函数的一部分)使用的SearchOption发生冲突。 - muttley91
8
既然问题中没有指定“由于冲突无法引用VisualBasic库”,因此Downvote并不应该被给予。你可以在代码中轻松解决这个问题。http://stackoverflow.com/questions/1317263/system-io-versus-visualbasic-fileio - jsmith
1
该方法似乎在内部使用SHFileOperation,它不处理长路径,并且将无法处理超过MAX_PATH的路径(即使使用\?\前缀)。 - Melvyn

19
以下解决方案比其他方案更简单:
using Shell32;

static class Program
{
    public static Shell shell = new Shell();
    public static Folder RecyclingBin = shell.NameSpace(10);

    static void Main()
    {
        RecyclingBin.MoveHere("PATH TO FILE/FOLDER")
    }
}

你可以使用此库来使用回收站的其他功能。
首先,不要忘记添加“Microsoft Shell Controls And Automation”库(从COM菜单中),以便使用Shell32命名空间。它将与您的项目动态链接,而不是与您的程序一起编译。

[1]: https://i.stack.imgur.com/erV


10
如果您专注于解决方案而不是在第一段评论其他答案,那么您的回答会更好。另外,为了清晰起见,我建议将“10”替换为“Shell32.ShellSpecialFolderConstants.ssfBITBUCKET”。同样,值得一提的是MoveHere方法的第二个参数,包括64(“尽可能保留撤消信息”)等选项。最后,附上MSDN文档链接作为完善。 - grek40
2
看起来调用MoveHere没有出现任何错误:在不存在的文件上调用它会悄无声息地失败!在路径超过MAX_CHARS的情况下,无论是否有“\?\”前缀,它也会悄无声息地失败... - Melvyn
它对我不起作用。它根本什么也不做,文件也没有被删除。 - Elmue

13
很遗憾,您需要使用Win32 API将文件移动到回收站。尝试以下代码,基于这个帖子。它利用通用的SHFileOperation函数通过Windows Shell进行文件系统操作。
在一个实用类中定义以下内容可能是最好的。
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto, Pack=1)]
public struct SHFILEOPSTRUCT
{
        public IntPtr hwnd;
        [MarshalAs(UnmanagedType.U4)] public int wFunc;
        public string pFrom;
        public string pTo;
        public short fFlags;
        [MarshalAs(UnmanagedType.Bool)] public bool fAnyOperationsAborted;
        public IntPtr hNameMappings;
        public string lpszProgressTitle;
}

[DllImport("shell32.dll", CharSet=CharSet.Auto)]
public static extern int SHFileOperation(ref SHFILEOPSTRUCT FileOp);

public const int FO_DELETE = 3;
public const int FOF_ALLOWUNDO = 0x40;
public const int FOF_NOCONFIRMATION = 0x10; // Don't prompt the user

如果您想使用它将文件发送到回收站进行删除,您需要像这样的内容:

var shf = new SHFILEOPSTRUCT();
shf.wFunc = FO_DELETE;
shf.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION;
shf.pFrom = @"C:\test.txt";
SHFileOperation(ref shf);

1
并且双重空终止字符串。 - sean e
1
SHFileOperation 不支持长路径,即使使用 \?\ 前缀,对于超过 MAX_PATH 的路径也会失败。 - Melvyn
请注意,这行代码 shf.pFrom = @"C:\test.txt"; 是错误的 - pFrom 必须是双重空终止符。您应该在文件中添加 \0,使代码变为 shf.pFrom = "C:\\text.txt\0";。请参阅 https://learn.microsoft.com/en-us/windows/desktop/api/shellapi/ns-shellapi-_shfileopstructa。 - lindexi

2
"最初的回答" 翻译成英文是 "Original Answer".

针对此问题,有一个内置库可供使用。

首先添加引用Microsoft.VisualBasic,然后添加以下代码:

FileSystem.DeleteFile(path_of_the_file,
                        Microsoft.VisualBasic.FileIO.UIOption.AllDialogs,
                        Microsoft.VisualBasic.FileIO.RecycleOption.SendToRecycleBin,
                        Microsoft.VisualBasic.FileIO.UICancelOption.ThrowException);

我找到了这个链接。最初的回答。

1

1
SHFileOperation 不支持长路径,即使使用 \?\ 前缀,对于超过 MAX_PATH 的路径也会失败。 - Melvyn

1

我使用这个扩展方法,然后我可以只使用DirectoryInfo或FileInfo并删除它。

public static class NativeMethods
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        struct SHFILEOPSTRUCT
    {
        public IntPtr hwnd;
        [MarshalAs(UnmanagedType.U4)]
        public int wFunc;
        public string pFrom;
        public string pTo;
        public short fFlags;
        [MarshalAs(UnmanagedType.Bool)]
        public bool fAnyOperationsAborted;
        public IntPtr hNameMappings;
        public string lpszProgressTitle;
    }
    private const int FO_DELETE = 0x0003;
    private const int FOF_ALLOWUNDO = 0x0040;           // Preserve undo information, if possible. 
    private const int FOF_NOCONFIRMATION = 0x0010;      // Show no confirmation dialog box to the user      


    [DllImport("shell32.dll", CharSet = CharSet.Auto)]
    static extern int SHFileOperation(ref SHFILEOPSTRUCT FileOp);

    static bool DeleteFileOrFolder(string path)
    {


        SHFILEOPSTRUCT fileop = new SHFILEOPSTRUCT();
        fileop.wFunc = FO_DELETE;
        fileop.pFrom = path + '\0' + '\0';            
        fileop.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION;


        var rc= SHFileOperation(ref fileop);
        return rc==0;
    }

    public static bool ToRecycleBin(this DirectoryInfo dir)
    {
        dir?.Refresh();
        if(dir is null || !dir.Exists)
        {
            return false;
        }
        else
            return DeleteFileOrFolder(dir.FullName);
    }
    public static bool ToRecycleBin(this FileInfo file)
    {
        file?.Refresh();

        if(file is null ||!file.Exists)
        {
            return false;
        }
        return DeleteFileOrFolder(file.FullName);
    }
}

一个调用它的示例可能是这样的:
private void BtnDelete_Click(object sender, EventArgs e)
{
    if(MessageBox.Show("Are you sure you would like to delete this directory?", "Delete & Close", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
        return;

    var dir= new DirectoryInfo(directoryName);
    dir.ToRecycleBin();

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接