Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Send File to RecyleBin, big file get permanent delete

Tags:

c#

.net

Alright, there are 2 ways to send a file to Recyle Bin in .net, either use Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile or use SHFileOperation. Both works good but they delete file permanently if it can't fit inside Recycle Bin. Is it somehow possible that it can throw Exception or return boolean value if file is too big or just simply do not delete it ? (Do not want default confirm dialog)

One way I got was to get the max size of Recycle Bin allowed for the volume then subtract the used size and check if file will get send to RB or deleted permanently, but it may get bad if deleting many files and check again and again.

Anything else I can try ?

like image 664
xmen Avatar asked Jan 28 '14 13:01

xmen


1 Answers

This question is not as simple as I thought in the beginning. However, I found it's still possible to be solved.

First of all, You need to know how much the recycle bins were used. The Win32's SHQueryRecycleBin can do it for you:

/// <summary>
/// Retrieves the size of the Recycle Bin and the number of items in it, of a specific drive
/// </summary>
/// <param name="pszRootPath">The path of the root drive on which the Recycle Bin is located</param>
/// <param name="pSHQueryRBInfo">A SHQUERYRBINFO structure that receives the Recycle Bin information</param>
/// <returns></returns>
[DllImport("shell32.dll")]
static extern int SHQueryRecycleBin(string pszRootPath, ref SHQUERYRBINFO pSHQueryRBInfo);

/// <summary>
/// Contains the size and item count information retrieved by the SHQueryRecycleBin function
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct SHQUERYRBINFO
{
    /// <summary>
    /// The size of the structure, in bytes
    /// </summary>
    public int cbSize;
    /// <summary>
    /// The total size of all the objects in the specified Recycle Bin, in bytes
    /// </summary>
    public long i64Size;
    /// <summary>
    /// The total number of items in the specified Recycle Bin
    /// </summary>
    public long i64NumItems;
}

Use the following demo code to retrieve this info:

const int S_OK = 0;
//string drivePath = @"C:\$RECYCLE.BIN\";
string drivePath = @"D:\$RECYCLE.BIN\";
SHQUERYRBINFO pSHQueryRBInfo = new SHQUERYRBINFO();
pSHQueryRBInfo.cbSize = Marshal.SizeOf(typeof(SHQUERYRBINFO));
int hresult = SHQueryRecycleBin(drivePath, ref pSHQueryRBInfo);
Console.WriteLine("{0} Drive {1} contains {2} item(s) in {3:#,##0} bytes", 
    hresult == S_OK ? "Success!" : "Fail!",
    drivePath, pSHQueryRBInfo.i64NumItems, pSHQueryRBInfo.i64Size);

Second, (at least on Win7) users can determine how much size they reserve for the recycle bin on (almost) every drive. So you need to know the maximum capacity of the recycle bin for every drive that we can get. This information can be found in the registry. But we also need to retrieve the guids for all the drives:

/// <summary>
/// Get from the registry all the drive guids
/// </summary>
static string[] GetDriveIds()
{
    const string registryPath = @"Software\Microsoft\Windows\CurrentVersion\Explorer\BitBucket\Volume\";
    RegistryKey reg = Registry.CurrentUser.OpenSubKey(registryPath);
    string[] readIn = reg.GetSubKeyNames();
    string[] driveIds = new string[readIn.Length - 1];
    Array.Copy(readIn, 1, driveIds, 0, readIn.Length - 1); // The first item must be removed
    return driveIds;
}

/// <summary>
/// Get and return the drive's recycle bin's MaxCapacity
/// </summary>
/// <param name="driveId">The guid of the specified drive</param>
/// <returns>The size in mega bytes</returns>
static int FindDriveCapacity(string driveId)
{
    const string registryPath = @"Software\Microsoft\Windows\CurrentVersion\Explorer\BitBucket\Volume\{0}\";
    RegistryKey reg = Registry.CurrentUser.OpenSubKey(
        string.Format(registryPath, driveId));
    return (int)reg.GetValue("MaxCapacity", 0);
}

With the following code you can retrieve the maximum capacity for every drive:

string[] driveIds = GetDriveIds();
int driveNo = 0;
foreach (string driveId in driveIds)
{
    Console.WriteLine("{0}. MaxCapacity of drive {1} is {2:#,##0} bytes",
        ++driveNo, driveId, FindDriveCapacity(driveId));
}

The last thing we have to do is map the guid with the drive letter. We still need to use registry:

/// <summary>
/// Map the drive letter mapped by the drive ID
/// </summary>
/// <param name="driveId">The guid of the drive</param>
static string MapDriveLetter(string driveId)
{
    const string registryPath = @"SYSTEM\MountedDevices";
    RegistryKey reg = Registry.LocalMachine.OpenSubKey(registryPath);
    string[] readIn = reg.GetValueNames();
    byte[] keyCode = {};
    Regex regGuid = new Regex(@"\{[^\}]+\}");
    Regex regDriveLetter = new Regex(@"[A-Z]:$");
    foreach (string keyRead in readIn) {
        if (regGuid.IsMatch(keyRead) && regGuid.Match(keyRead).Value == driveId )
            keyCode = (byte[])reg.GetValue(keyRead, null);                
    }
    foreach (string keyRead in readIn)
    {
        byte[] codeRead = (byte[])reg.GetValue(keyRead, null);
        if (!regGuid.IsMatch(keyRead) && keyCode.SequenceEqual(codeRead))
        {
            if (regDriveLetter.IsMatch(keyRead)) // Get the drive letter in the form "E:"
                return regDriveLetter.Match(keyRead).Value;
        }
    }
    return string.Empty;
}

Simply pass the guid and you can get the drive number:

string code = MapDriveLetter("{f4b90148-66f6-11e3-9ac5-806e6f6e6963}");

With all the information combined together, you should be able to tell on what drive how much the size of a file or files might be deleted permanently by the system.

like image 109
Johnny Avatar answered Sep 23 '22 12:09

Johnny