kernal32.dll FindFirstFileからファイル情報を取得することにより、ディレクトリの全体的なサイズを返す32ビットのc#アプリケーションを作成しています。これにより、各ディレクトリを通常の方法で列挙することに成功し、リソースの使用量を非常に低く抑えることができます。
これがどのように機能するかの概要は次のとおりです。
- ステップ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);
        }
    }
これらの2つのメソッドは、次のコードを使用して呼び出されます。
 ThreadPool.SetMaxThreads(30, 500);
 Thread.CurrentThread.Priority = ThreadPriority.Normal;
 rootDirectory = share["Path"].ToString();
 recurseDirectories(share["Path"].ToString(), true);
 while (numThreadsQueued != directoriesProcessed)
 {
        Thread.Sleep(1000);
 }
このコードは、ほとんどのディレクトリを列挙している間、問題なく実行されました。CPUを3%未満に保ち、15 MBのメモリを使用しながら、約8分で合計ファイルサイズとファイル数を取得する3TBファイル共有を再帰的に実行できます。
今問題が来る...
小さなディレクトリ(1〜200 GB)のサイズを取得するとき、ディレクトリのプロパティを見るときにWindowsが言うこととの大きな違いは見られません。ただし、大きなディレクトリ(2〜3 TB)のサイズを取得するときに、いくつかの大きな違いに気づきました。
例えば:
別のサーバーに複製されたDFSRであるディレクトリD:\TestDirを見ているとしましょう。Windowsによると、このディレクトリはディスク上で2,949,944,019,217バイト、つまり2,974,186,774,528バイト(それぞれ2.68TBまたは2.70TB)です。私のプログラムによると、このディレクトリは3,009,619,048,759バイトまたは2.737TBです。FSRMによると、同じディレクトリでのクォータ設定の使用量は2.71TBです。
違いの一部は、Windowsのサイズに隠しファイルが含まれていないことが原因ですが、ディレクトリ内の隠しファイルの合計サイズ(87GB)をWindowsの値に追加すると、最大2.78 GBになりますが、これはまだ私の値とは異なります。誰かが私がこれらのサイズの違いを引き起こしている他の何かに光を当てることができますか?また、FSRMがクォータの使用を決定する方法を知っている人はいますか?
最終的には、FSRMクォータをデータを使用する監視システムに置き換えたいのですが、データがWindowsの指示と一致しない場合、ディスク使用量について誤ったアラームが発生する可能性があります。