使用单个 Windows 提示删除多个文件(文件系统)

Delete Multiple Files with Single Windows Prompt (FileSystem)

我有一个显示文件夹中文件的 WPF 应用程序。用户可以 select 多个文件并 select 删除它们,目前我正在使用此逻辑以及使用 VisualBasic.FileIO 库中的文件系统:

foreach (Item item in items)
{
     if (item.IsDirectory)
     {
          FileSystem.DeleteDirectory(item.FullPath, UIOption.AllDialogs, RecycleOption.SendToRecycleBin);
     }
     else
     {
          FileSystem.DeleteFile(item.FullPath, UIOption.AllDialogs, RecycleOption.SendToRecycleBin);
     }
} 

这里的问题是,如果用户打开了 Windows 选项“显示删除确认对话框”:

他们会得到每个文件的 Windows 提示。

我希望他们得到一个这样的提示:

有办法吗?

即使涉及到一些WinAPI函数的PInvoke?

通过PInvoke,我们可以使用SHFileOperation with the FO_DELETE函数将文件系统对象发送到回收站。根据文档,我们可以通过用 NULL 字符连接它们来一次发送多个路径:

Although this member is declared as a single null-terminated string, it is actually a buffer that can hold multiple null-delimited file names. Each file name is terminated by a single NULL character. The last file name is terminated with a double NULL character ("[=12=][=12=]") to indicate the end of the buffer.

与其从头开始编写所有内容,不如使用 this answer 中的部分代码并调整它以使用多个路径。我们会有这样的东西:

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);

    public static bool SendToRecycleBin(string path, FileOperationFlags flags)
    {
        return SendToRecycleBin(new[] { path }, flags);
    }

    public static bool SendToRecycleBin(IList<string> paths, FileOperationFlags flags)
    {
        try
        {
            var fs = new SHFILEOPSTRUCT
            {
                wFunc = FileOperationType.FO_DELETE,
                pFrom = string.Join("[=10=]", paths) + '[=10=]' + '[=10=]',
                fFlags = FileOperationFlags.FOF_ALLOWUNDO | flags
            };
            SHFileOperation(ref fs);
            return true;
        }
        catch (Exception)
        {
            return false;
        }
    }
}

用法:

FileOperationAPIWrapper.SendToRecycleBin(items,
    FileOperationAPIWrapper.FileOperationFlags.FOF_WANTNUKEWARNING);