应用程序之间的Windows目录大小差异

时间:2012-12-31 18:39:28

标签: c# windows

我正在编写一个32位的c#应用程序,它通过从kernal32.dll FindFirstFile获取文件信息来返回目录的总大小。这已经胜过以常规方式枚举每个目录,并且我能够将资源使用保持在极低水平。

快速概述其工作原理如下:

  • 步骤1 - 枚举获取所有子目录的根目录,并使用FindFirstFile收集此目录中每个文件的大小信息。
  • 步骤2 - 生成子线程(最多20个)以对子目录执行步骤1
  • 步骤3 - 递归直到目录耗尽并且已收集所有文件信息。

这可以在下面的代码示例中看到,其中FileSystem.GetFiles是我的类,它使用kernal32方法来获取文件信息。

      private static void recurseDirectories(string directoryA, bool paramInitialPass)
    {
        try
        {
            string[] currentDirs;
            if (paramInitialPass)
            {
                currentDirs = new string[1];
                currentDirs[0] = rootDirectory;
            }
            else
                currentDirs = Directory.GetDirectories(directoryA);

            for (int i = 0; i < currentDirs.Length; i++)
            {

                string threadInfo = currentDirs[i];
                numThreadsQueued++;
                ThreadPool.QueueUserWorkItem(new WaitCallback(getDirectoryFileInformation), (object)threadInfo);
                while (numThreadsQueued - directoriesProcessed > 20)
                {
                    Thread.Sleep(30);
                }
                if (paramInitialPass)
                    recurseDirectories(directoryA, false);
                else
                    recurseDirectories(currentDirs[i], false);
            }
        }
        catch
        {

        }
        return;
    }


    private static void getDirectoryFileInformation(object paramDirectoryFilePathA)
    {
        try
        {
            string directoryPathA = (string)paramDirectoryFilePathA;
            List<FileData> filesDirectoryA = new List<FileData>();
            if (Directory.Exists(directoryPathA))
            {
                    filesDirectoryA = FileSystem.GetFiles(directoryPathA);
            }
            foreach(FileData file in filesDirectoryA)
            {
                Interlocked.Add(ref sizeOfFiles, file.Size);
                Interlocked.Increment(ref numberOfFiles);
            }               
        }
        catch (Exception e)
        {

        }
        finally
        {
            Interlocked.Increment(ref directoriesProcessed);
        }
    }

使用以下代码调用这两个方法:

 ThreadPool.SetMaxThreads(30, 500);
 Thread.CurrentThread.Priority = ThreadPriority.Normal;
 rootDirectory = share["Path"].ToString();
 recurseDirectories(share["Path"].ToString(), true);
 while (numThreadsQueued != directoriesProcessed)
 {
        Thread.Sleep(1000);
 }

此代码在枚举大多数目录时执行完美。我能够在大约8分钟内递归3TB文件共享,获得总文件大小和文件数,同时保持cpu低于3%并使用15MB内存。

现在出现问题......

获取小目录(1-200 GB)的大小时,我发现在查看目录属性时,Windows没有显示任何重大差异。但是,在获取大型目录(2-3TB)的大小时,我注意到了一些重大差异。

例如:

说我正在查看目录D:\ TestDir,它是DFSR复制到另一台服务器。 Windows称此目录为2,949,944,019,217字节,或磁盘上的2,974,186,774,528字节(分别为2.68 TB或2.70 TB)。 我的程序说这个目录是3,009,619,048,759字节或2.737 TB。 FSRM表示同一目录上的配额设置使用了2.71 TB。

我知道差异部分是由于Windows不包含其大小的隐藏文件,但是当我将目录中的隐藏文件的总大小(87GB)添加到Windows值时,我得到~2.78 GB仍然不同于我的价值。任何人都可以了解一下我造成这些尺寸差异的其他因素吗?此外,有谁知道FSRM如何确定配额使用?

最终我想使用我的数据用监控系统替换FSRM配额,但如果我的数据与Windows所说的不一致,我可能会收到有关磁盘使用情况的错误警报。

2 个答案:

答案 0 :(得分:1)

经过深入测试后,这最终成了kernal32.dll FindFirstFile方法的错误:

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern SafeFindHandle FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData)

此函数返回一个类&#34; WIN32_FIND_DATA&#34;其中包含有关特定文件的信息,包括名称,大小,上次修改时间等。我运行了一个测试,我将此函数返回的大小与System.IO.FileInfo类返回的大小进行比较,发现一些明显的差异。非常小的文件集。对包含约150万个文件的文件共享运行时,两个文件的大小明显不同,如下所示:

文件1
根据FileInfo的大小:18158717658字节
根据WIN32_FIND_DATA的大小:978848478字节

文件2
根据FileInfo的大小:18211490304字节
根据WIN32_FIND_DATA的大小:1031621124字节

在这两种情况下,尺寸差异几乎都是16 GB。

要解决此问题,我将继续使用Kernal32.dll函数来获取文件路径,但使用FileInfo来获取大小。这似乎在不影响性能的情况下产生了良好的效果。

答案 1 :(得分:0)

您提到您的应用程序编译为32位。你是在64位系统上运行它吗?您可能遇到文件系统重定向,例如,当32位应用程序尝试读取C:\Windows\System32时您实际获得C:\Windows\SysWOW64。您可能需要p / invoke Wow64DisableWow64FsRedirection

因此,FileInfo可能会正确报告非常大的文件的大小,但尽管这可能会使您的答案保持一致,但仍然不正确。你为什么还要使用p / invoke?

此外,NTFS文件系统支持单个文件具有多个目录条目的硬链接。但它只使用磁盘空间一次。您可以通过读取“链接计数”元数据并将文件大小除以该字段来处理此问题。在这种情况下,您需要p / invoke Win32 API。您可能还希望使用GetFileInformationByHandleEx(在打开具有查询权限的文件后)而不是WIN32_FIND_DATA结构中的信息。

这个问题比它看起来更难。