将文件发送至回收站,大文件会被永久删除。

17

好的,在.net中将文件发送到回收站有两种方法,可以使用Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile或使用SHFileOperation。但是,如果文件无法放入回收站,则两种方法都会永久删除文件。

是否可能以某种方式抛出异常或返回布尔值,如果文件太大或仅简单地不删除它?(不想要默认的确认对话框)

我找到的一种方法是获取卷允许的回收站最大大小,然后减去已使用的大小,并检查文件是否将被发送到RB或永久删除,但如果删除多个文件并反复检查,则可能会变糟。

还有其他什么尝试的吗?


你需要一遍又一遍地检查吗?你能获取最大的大小并减去已使用的大小,然后继续减去您正在回收的文件的大小吗? - Michael B
可以,但也有可能是另一个应用程序正在删除或用户手动删除文件(发送到RB)。如果删除一个文件夹,情况会更加复杂。 - xmen
如果你想保证文件不被删除,回收站似乎不是正确的位置。回收站的策略取决于用户(和管理员),而不是你的程序。 - Damien_The_Unbeliever
假设您的程序已成功实现了您正在寻找的“回收站或不删除”功能。在此代码运行并报告成功转移到回收站之后,用户决定(也许他们感到无聊)去清空他们的回收站。最终结果是您认为您已经成功了,但文件仍然消失了(就像消失了一样)。 - Damien_The_Unbeliever
这个需要在WinXP上工作吗?你能使用IFileOperation吗? - wonko realtime
显示剩余2条评论
3个回答

5
这个问题并不像我最初想象的那样简单。然而,我发现它仍然是可以解决的。
首先,您需要知道回收站的使用情况。Win32 的 SHQueryRecycleBin 可以为您完成此操作:
/// <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;
}

使用以下演示代码来检索此信息:
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);

其次,在Windows 7上(至少在Windows 7上),用户可以确定他们在(几乎)每个驱动器上为回收站保留多少空间。因此,您需要知道我们可以获取的每个驱动器的回收站的最大容量。这些信息可以在注册表中找到。但是,我们还需要检索所有驱动器的GUID:

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

使用以下代码,您可以检索每个驱动器的最大容量:

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

我们需要做的最后一件事情是将GUID与驱动器字母进行映射。我们仍需使用注册表: ```html

最后一步需要使用注册表来将GUID映射到驱动器字母。

```
/// <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;
}

只需传递 GUID,您就可以获得驱动器号:

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

综合所有信息,你应该能够确定系统会永久删除哪个驱动器上多大的文件或文件。


确实,我确实做过这样的事情。获取这些信息从来不是问题,问题是即使使用这种方法,文件仍然可能会被永久删除。例如,在删除多个文件时,你会得到总大小并检查是否能适合RB中。但是当你删除多个文件时(不同的路径,而不是子目录),如果像Windows资源管理器这样的其他应用程序也将文件发送到RB中,它们太大了无法让我们的文件适合内部。然后我们其余要删除的文件将被永久删除。这就是为什么我问是否还有其他尝试的方法。无论如何,谢谢:) +1 - xmen
你不能为用户做所有的事情;在你的情况下,他们决定删除什么,而不是你。否则,如果用户在你的程序运行时切断电源,这也是你的责任吗?提示用户正在删除文件,并警告他们无法保证从RB中恢复所有文件,因为其他程序也可以访问RB。我认为这已经足够了。 - Johnny
当然可以尽可能地写更多的代码行。用户永远不会错,即使是他/她的错误,他们也会责怪开发者。切断电源非常明显。但删除不是,普通用户甚至不会阅读确认对话框消息,他们只需在按下删除后按下“是”或“否”按钮。如果他们在RB中找不到自己的文件,那将是一个问题。这适用于每个人,我们的大脑会忙碌并犯错,但应用程序不应犯错:) 我希望我表达清楚了我的观点。 - xmen
1
唯一的解决方案可能是锁定RB,如果可能的话。但我不这样认为。 - Johnny
我突然想到一件事。也许你可以创建自己的RB,这样其他程序就无法访问它。然后你可以决定何时清空你的RB,你的用户可以随意恢复任何文件。 - Johnny

4
您的帖子中提到了删除文件,但如果文件太大无法发送到回收站,则不想删除它们。您的目标有些不清楚,但似乎您在批量删除时不想针对每个文件检查回收站。若要实现用户可以恢复这些文件,那么在一组文件中哪些文件进入回收站而哪些不进呢?
您可以收集所有文件的大小,然后与回收站进行比较。如果太大,则不删除任何文件(或显示永久删除提示)。
除非您正在尝试重新制作“文件资源管理器”,否则建议您改用应用程序中的垃圾桶文件夹,这样可以更加灵活地控制文件。

获取总文件大小也可能失败。假设有1000个文件需要放入回收站,程序得到了总大小并且可以适合回收站,但同时资源管理器或其他应用程序将大文件发送到回收站。现在,我的程序中的文件将永远无法到达回收站。是的,创建自己的垃圾文件夹是一个好主意,但我不能实现它,我想尽可能接近Windows资源管理器的删除方式。 :) 谢谢您的回答 +1 - xmen
我发现了这个链接:https://dev59.com/B3A75IYBdhLWcg3wdI37。我想在我的电脑上测试一下,但是如果我从Windows资源管理器中删除文件,而且该文件太大以至于无法放入回收站,那么Windows会永久删除它。对我来说这很奇怪。 - bdn02
@bdn02:右键单击回收站>属性。然后为您的卷选择自定义大小。 - xmen
感谢+1。不幸的是,这也适用于资源管理器。我不相信有一种方法可以锁定RB。在删除过程中,总有可能有人将某些东西移动到回收站中。您可以尝试删除,如果您的文件因为在删除过程中被占满而失败,则回滚您的删除,然后使用永久删除对话框继续。我认为资源管理器只会移动到RB,直到遇到问题,然后弹出对话框。 - Mr. MonoChrome

3

不要删除文件,而是将其重命名为不同的路径,例如app_history文件夹。重命名方法有两个主要点。

您可以重命名文件的路径部分,如果路径在同一磁盘驱动器上,则运行非常快,因为只更改了磁盘上文件的指针。如果路径指向不同的驱动器,则文件将被移动。在Windows操作系统中,所有这些都会自动发生。

该程序无法物理删除文件,因此永远不会因删除无法恢复的文件而受到负面影响。

这里有一些关于重命名方法的文档请记住,在Windows中,重命名可以更改文件的路径。


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