2012-12-31 28 views
2

我正在編寫一個32位的c#應用程序,通過從kernal32.dll FindFirstFile獲取文件信息來返回目錄的總體大小。這已經勝過以常規方式枚舉每個目錄,並且我能夠保持資源使用非常低。應用程序之間的Windows目錄大小差異

是如何工作的快速概述如下:

  • 第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)我看不出什麼,從窗戶往外看的目錄屬性時說,任何重大差異。然而,我已經注意到,在獲得大型目錄的大小(2-3TB)時有一些重大差異。

例如:

說我在看目錄d:\ TESTDIR這是DFSR複製到另一臺服務器。 Windows說這個目錄是2,949,944,019,217字節,或磁盤上的2,974,186,774,528字節(分別是2.68TB或2.70TB)。 我的程序說這個目錄是3,009,619,048,759字節或2.737 TB。 FSRM表示同一目錄上的配額設置有2.71 TB的使用量。

我知道這種差異部分是由於Windows不包含隱藏文件的大小,但是當我將目錄(87GB)中的隱藏文件的總大小添加到Windows值時,我得到了〜2.78 GB,這仍然不同從我的價值。任何人都可以闡明我還有什麼可以導致這些尺寸差異?另外,有誰知道FSRM如何確定配額使用情況?

最後我想用我的數據替換監視系統的FSRM配額,但是如果我的數據不符合Windows的說法,我可能會在磁盤使用情況下獲得虛假警報。

+0

根本沒有看過它,但是對於小文件,一個是報告數據的實際大小,另一個是報告分配磁盤空間的大小?我相信一個文件的最小分配大小是4K,不管數據有多少。 – Bobson

+0

感謝Bobson的評論,我確信Windows的「磁盤大小」將此考慮在內,從而產生2.70 TB的值。我這樣說是因爲如果您查看4K以下文件的屬性,它會說「大小:x字節,磁盤上的大小:4.00 KB」) – vesuvious

回答

1

經過一番深入檢測該結束了與kernal32.dll方法用FindFirstFile錯誤:

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

該函數返回一個包含一個特定的文件,包括名稱,大小信息類「WIN32_FIND_DATA」最後修改時間等等。我運行了一個測試,在這裏我比較了這個函數返回的大小和System.IO返回的大小。FileInfo類,並在一小組文件中發現了一些明顯的差異。當運行這個對包含文件共享〜150萬個文件,這兩個文件有顯著不同尺寸返回如下:根據

FILE 1
尺寸對FileInfo:18158717658個字節根據WIN32_FIND_DATA
大小:978848478個字節

根據FileInfo的FILE 2
大小:18211490304個字節
根據WIN32_FIND_DATA大小:1031621124個字節

在兩種情況下在尺寸上的差別幾乎是正好16 GB。

要解決此問題,我將繼續使用Kernal32.dll函數獲取文件路徑,但使用FileInfo獲取大小。這似乎在不影響性能的情況下產生好的結果。

+0

要結束,很好發現FileInfo或者說是WinAPI函數GetFileAttributesEx這是FileInfo在內部使用時的結果)給出了不同的結果 –

+0

我同意,儘管我找到了解決辦法,我不會考慮解決這個問題。我可以發現的任何信息 – vesuvious

0

你提到你的應用程序編譯爲32位。你在64位系統上運行它嗎?您可能遇到文件系統重定向,例如,當32位應用程序嘗試讀取C:\Windows\System32時,您實際上得到C:\Windows\SysWOW64。您可能需要p/invoke Wow64DisableWow64FsRedirection

因此,FileInfo可能會處理正確報告非常大的文件的大小,但雖然這可能會使您的答案一致,他們仍然是不正確的。爲什麼你仍然使用p/invoke?

此外,NTFS文件系統支持硬鏈接,其中單個文件具有多個目錄條目。但它只爲其內容使用磁盤空間一次。您可以通過閱讀「鏈接計數」元數據並將文件大小除以該字段來處理此問題。在這種情況下,您將需要p /調用Win32 API。您可能還想使用GetFileInformationByHandleEx(打開具有查詢權限的文件後)而不是WIN32_FIND_DATA結構中的信息。

這個問題比看起來更困難。

+0

我的應用程序是32位的,我目前的測試是在64位系統上,我的代碼通過檢查進程是否是64位並使用Wow64DisableWow64FsRedirection和Wow64RevertWow64FsRedirection方法來處理文件系統重定向。硬連接是一個好主意,我會寫一些代碼來測試這個理論。 – vesuvious

相關問題