Enviar un archivo a la papelera de reciclaje

83

Actualmente estoy usando la siguiente función

file.Delete();

Pero, ¿cómo puedo usar esta función para enviar un archivo a la papelera de reciclaje en lugar de simplemente eliminarlo por completo?

muttley91
fuente
3
El enlace de @ UweKeim ya no está disponible, puede encontrar una versión en formato .chm de MSDN Magazine (diciembre de 2007) aquí , se llama al artículo .NET Matters: IFileOperation in Windows Vistay se encuentra en la Columnscarpeta.
jrh
El artículo no se abre en el archivo .chm para mí. Este enlace funciona: docs.microsoft.com/en-us/archive/msdn-magazine/2007/december/…
RandomEngy
También debe agregar FOFX_RECYCLEONDELETE = 0x00080000a los indicadores de operación, y ese indicador solo es compatible con Windows 8 o superior.
RandomEngy

Respuestas:

52

NOTA: Esto tampoco funciona con aplicaciones interactivas que no sean de IU como Servicios de Windows

Este contenedor puede proporcionarle la funcionalidad necesaria:

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);
        }
    }
Eugene Cheverda
fuente
No entiendo cómo usar esto ... ¿podrías explicarme?
muttley91
4
Quite Pack = 1 si está compilando para una plataforma de 64 bits (de lo contrario fallará). Sin Pack = 1 especificado, esto funcionará tanto para 32 bits como para 64 bits. pinvoke.net/default.aspx/Structures/SHFILEOPSTRUCT.html
Sean
1
Cuando se usa Pack = 1, se lanza una AccessViolationException. Quitarlo hizo el truco. Windows de 64 bits por cierto
P1nGu1n
1
¿Cuáles son los requisitos para ejecutar este código? Elimino Pack = 1 pero todavía no se compila. DllImport, DllImportAttribute, MarshalAs, MarshalAsAttribute, StructLayout, StructLayoutAttribute no existen como un espacio de nombres. Cualquier ayuda, por favor, gracias :)
puretppc
1
SHFileOperation no maneja rutas largas y fallará con rutas más largas que MAX_PATH (incluso con un prefijo \\? \).
Melvyn
155

Utilice FileSystem.DeleteFile y especifique la opción RecycleOption correcta .

Si bien esto funcionará con aplicaciones interactivas de IU, no funcionará con aplicaciones interactivas que no sean de IU, como una aplicación de servicio de Windows.

NG.
fuente
17
@noldorin Esta es una solución perfectamente buena, no merece un voto negativo. Me gustaría una referencia sobre por qué el acceso a la biblioteca VisualBasic es "feo".
jsmith
7
@noldorin: Especialmente en este caso, Microsoft.VisualBasic.FileIO.FileSystemhace básicamente lo mismo que el ejemplo publicado aquí usando SHFileOperation.
Dirk Vollmar
18
@Noldorin: Feo, ¿eh? Para mí, la forma de WinAPI es mucho más fea; además, tienes más posibilidades de estropear algo. Personalmente, no me gusta la sintaxis de VB, pero en ensamblajes es solo ILpara que no me importe. El ensamblado VB llama a la misma función WinAPI por cierto.
Jaroslav Jandek
7
@Noldorin: ¿Obsoleto? ¿Ha confundido la asamblea Microsoft.VisualBasic.Compatibilitypor casualidad? Ese lo evitaría. No parece que vaya a quedar obsoleto en el corto plazo (se usa en el motor de informes RDL, etc.).
Jaroslav Jandek
6
@Noldorin: Usar un ensamblaje de marco integrado parece una mejor solución que usar un mapeo de estilo duro en shell32.dll. Al usar ensamblajes de marco, el cambio es portátil y obtiene evoluciones posteriores. Al mapear las bibliotecas del sistema, se arriesga a quedar obsoleto cualquier día ...
fredlegrain
41

De MSDN :

Agregue una referencia al ensamblado Microsoft.VisualBasic. La clase necesaria se encuentra en esta biblioteca.

Agregue esta declaración using al principio del archivo using Microsoft.VisualBasic.FileIO;

Úselo FileSystem.DeleteFilepara eliminar un archivo, tiene la opción de especificar papelera de reciclaje o no.

Úselo FileSystem.DeleteDirectorypara eliminar un directorio con la opción de especificar si desea enviarlo a la papelera de reciclaje o no.

MrGrumpy
fuente
El problema de incluir Microsoct.VisualBasic es que entra en conflicto con mi uso de SearchOption en otra parte de mi programa (parte de la función GetFiles ()).
muttley91
8
@rar Downvote todavía no se merece ya que no se especificó en la pregunta que "No se puede hacer referencia a la biblioteca VisualBasic debido a un conflicto". Lo cual podría resolver fácilmente en su código. stackoverflow.com/questions/1317263/…
jsmith
1
Este método parece utilizar internamente SHFileOperation, que no maneja rutas largas y fallará con rutas más largas que MAX_PATH (incluso con un prefijo \\? \).
Melvyn
17

La siguiente solución es más sencilla que las demás:

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")
    }
}

Puede utilizar otras funcionalidades de la papelera de reciclaje utilizando esta biblioteca.

Primero, no olvide agregar la biblioteca "Microsoft Shell Controls And Automation" (desde el menú COM), para poder usar el Shell32espacio de nombres. Se vinculará dinámicamente a su proyecto, en lugar de compilarse junto con su programa.

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

sɐunıɔ ןɐ qɐp
fuente
8
Tu respuesta sería mejor si te enfocas en tu solución en lugar de comentar otras respuestas en el primer párrafo. Además, en aras de la claridad, reemplazaría 10por Shell32.ShellSpecialFolderConstants.ssfBITBUCKET. Podría valer la pena mencionar el segundo parámetro MoveHere, con respecto a opciones como 64 ("Conservar la información de deshacer, si es posible"). Vincular algunas fuentes de documentación de MSDN sería un buen acabado.
grek40
2
Parece que la llamada a MoveHere no muestra ningún error: llamar a un archivo no existente falla silenciosamente. También falla silenciosamente en rutas más largas que MAX_CHARS, con o sin un prefijo "\\? \" ...
Melvyn
13

Desafortunadamente, debe recurrir a la API de Win32 para eliminar un archivo a la Papelera de reciclaje. Pruebe el siguiente código, basado en esta publicación . Hace uso de la SHFileOperationfunción genérica para las operaciones del sistema de archivos a través del Shell de Windows.

Defina lo siguiente (en una clase de utilidades probablemente sea mejor).

[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

Y para usarlo para eliminar un archivo, enviándolo a la Papelera de reciclaje, desea algo como:

var shf = new SHFILEOPSTRUCT();
shf.wFunc = FO_DELETE;
shf.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION;
shf.pFrom = @"C:\test.txt";
SHFileOperation(ref shf);
Noldorin
fuente
1
y doble nulo termina la cadena.
sean e
1
SHFileOperation no maneja rutas largas y fallará con rutas más largas que MAX_PATH (incluso con un prefijo \\? \).
Melvyn
Tenga en cuenta que esta línea shf.pFrom = @"C:\test.txt";es incorrecta: pFrom debe tener una terminación nula doble. Debe agregar \0el archivo shf.pFrom = "C:\\text.txt\0";. Ver docs.microsoft.com/en-us/windows/desktop/api/shellapi/…
lindexi
1

Puede DllImport SHFileOperationpara hacer esto.

Brian R. Bondy
fuente
1
SHFileOperation no maneja rutas largas y fallará con rutas más largas que MAX_PATH (incluso con un prefijo \\? \).
Melvyn
1

Utilizo este método de extensión, luego puedo usar un DirectoryInfo o FileInfo y eliminarlo.

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

una muestra de cómo llamarlo podría ser este:

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

}
Walter Vehoeven
fuente
-1

Hay una biblioteca incorporada para esto.

Primero agregue la referencia Microsoft.VisualBasic Luego agregue este código:

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

He encontrado esto aquí .

Maifee Ul Asad
fuente