Deleting file to recycle bin on Windows x64 in C#
Asked Answered
W

2

5

I've got this class which seems to work quite well on non-64bit.

using System;
using System.Runtime.InteropServices;

namespace DeleteToRecycleBin
{
/// <summary>
/// Send files directly to the recycle bin.
/// </summary>
public class RecybleBin
{

    /// <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, Pack = 1)]
    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 readonly bool fAnyOperationsAborted;
        public readonly IntPtr hNameMappings;
        public readonly 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
        {
            SHFILEOPSTRUCT fs = new SHFILEOPSTRUCT
                                    {
                                        wFunc = FileOperationType.FO_DELETE,
                                        pFrom = path + '\0' + '\0',
                                        fFlags = FileOperationFlags.FOF_ALLOWUNDO | flags
                                    };

            // important to double-terminate the string.
            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 SendSilent(string path)
    {
        return Send(path, FileOperationFlags.FOF_NOCONFIRMATION | FileOperationFlags.FOF_NOERRORUI | FileOperationFlags.FOF_SILENT);

    }
}

}

Any way to fix it so it works good on x64 too? I've tried chaning ushort to ulong and couple of other modifications but it doesn't work.

I know there are other solutions that require reference to Microsoft.VisualBasic but i would prefer p/invoke way.

CORRECT ANSWER IS:

Under x64, the SHFILEOPSTRUCT must be declared without the Pack = 1 parameter, or it will fail. This is a real pain if you want your code to be platform independent, as you have to declare two separate structures, one with Pack = 1, and one without. You then have to declare two different SHFileOperation calls, one for each of the structures. Then you have to decide which one to call depending on whether you are running on 32 or 64 bit.

Waikiki answered 26/2, 2010 at 15:23 Comment(1)
As stated in PInvoke site: Don't declare a value for the Pack size. If you omit it, the correct value is used when marshaling and a single SHFILEOPSTRUCT can be used for both 32-bit and 64-bit operation.Dagan
Z
2

Have you looked at the PInvoke site? It has a slight different definition for the FILEOPSTRUCT type, forcing Unicode for one thing. I wonder if the charset = auto is confusing things...like it defaults to ANSI on 32 bit, but Unicode on 64 bit and something is going wrong somewhere in the middle.

EDIT; Also, the Visual Basic reference approach is a simple one...I know people have an aversion to it for some reason, but the relevant DLLs are still part of the core framework, so you won't be adding any new dependencies.

Zoophilia answered 26/2, 2010 at 17:43 Comment(5)
The problem lays within [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]. For 64bit you need to remove Pack =1 and it works!!! Since you pointed me to PInvoke site it even says it there (right in the bottom of the article saying to remove Pack = 1 when it's 64bit os!Waikiki
Ha ha, I didn't even scroll down that far, I just saw the differences in the main structure definition. Still, it seems a solution of sorts, seems a bit awkward though.Zoophilia
I'm using 64bit Windows 7 with Pack = 1 for FILEOPSTRUCT and it works with me without any problems :|Vienne
Note, this will not work with non ui interactive apps like windows services. See [MSDN] (msdn.microsoft.com/en-us/library/windows/desktop/…) for details on the UNDO flag which works only within active sessions.Disentitle
As stated in PInvoke site: Don't declare a value for the Pack size. If you omit it, the correct value is used when marshaling and a single SHFILEOPSTRUCT can be used for both 32-bit and 64-bit operation.Dagan
C
8

As strange as it seems, .NET already has functions to delete to the Recycle Bin... but they're in the Microsoft.VisualBasic namespace. Specifically, Microsoft.VisualBasic.FileIO.

using Microsoft.VisualBasic.FileIO;

// class declaration, method start here

// Send file to recycle bin without dialog
FileSystem.DeleteFile("pathToFile", UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin);

// Send file to recycle bin without dialog
FileSystem.DeleteFile("pathToFile", UIOption.AllDialogs, RecycleOption.SendToRecycleBin);

// Directories are the same, but with DeleteDirectory instead
Concatenate answered 26/2, 2010 at 15:45 Comment(4)
I do know this solution but would prefer the one without using reference to VisualBasic.Waikiki
@Waikiki Why do you want to avoid referencing Microsoft.VisualBasic?Wilkerson
Well I don't have perfect reasoning but just preference. If i won't be able to fix the code I have i will use visual basic reference but i guess it should be possible to fix it. It's probably some difference in int32 vs int64 or similar issue.Waikiki
Note, this will not work with non ui interactive apps like windows services. See [MSDN] which specifies that the recycle option only work in interactive appsDisentitle
Z
2

Have you looked at the PInvoke site? It has a slight different definition for the FILEOPSTRUCT type, forcing Unicode for one thing. I wonder if the charset = auto is confusing things...like it defaults to ANSI on 32 bit, but Unicode on 64 bit and something is going wrong somewhere in the middle.

EDIT; Also, the Visual Basic reference approach is a simple one...I know people have an aversion to it for some reason, but the relevant DLLs are still part of the core framework, so you won't be adding any new dependencies.

Zoophilia answered 26/2, 2010 at 17:43 Comment(5)
The problem lays within [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]. For 64bit you need to remove Pack =1 and it works!!! Since you pointed me to PInvoke site it even says it there (right in the bottom of the article saying to remove Pack = 1 when it's 64bit os!Waikiki
Ha ha, I didn't even scroll down that far, I just saw the differences in the main structure definition. Still, it seems a solution of sorts, seems a bit awkward though.Zoophilia
I'm using 64bit Windows 7 with Pack = 1 for FILEOPSTRUCT and it works with me without any problems :|Vienne
Note, this will not work with non ui interactive apps like windows services. See [MSDN] (msdn.microsoft.com/en-us/library/windows/desktop/…) for details on the UNDO flag which works only within active sessions.Disentitle
As stated in PInvoke site: Don't declare a value for the Pack size. If you omit it, the correct value is used when marshaling and a single SHFILEOPSTRUCT can be used for both 32-bit and 64-bit operation.Dagan

© 2022 - 2024 — McMap. All rights reserved.