Check empty folder with IsDirectoryEmplty Win32 API

在判斷目錄是否為空這邊,.NET 4.0 以前,很多人都會很直覺的去使用 Directory.GetFiles 、Directory.GetDirectories 、或 Directory.GetFileSystemEntries 方法,用個數判斷是否為空。

1
2
3
4
5
public bool IsDirectoryEmplty(string directory)
{
//return !Directory.GetFiles(directory).Any() && !Directory.GetDirectories(directory).Any();
return !Directory.GetFileSystemEntries(directory).Any();
}


用這樣的方式處理,必須等待所有檔案都找出來後回傳才能進行空目錄的判斷,效能十分的低落。

在 .NET 4.0 後可改用 Directory.EnumerateFiles 、Directory.EnumerateDirectories 、或 Directory.EnumerateFileSystemEntries 替代。

1
2
3
4
5
public bool IsDirectoryEmplty(string directory)
{
//return !Directory.EnumerateFiles(directory).Any() && !Directory.EnumerateDirectories(directory).Any();
return !Directory.EnumerateFileSystemEntries(directory).Any();
}


相較於 .NET 4.0 以前的處理方式,檔案變成找到就立刻回傳,因此我們找到一個檔案就可以將搜尋的動作中斷,效能問題獲得了改善。
雖然效能的問題解決了,不過像這樣的處理方式會受限於 .NET Framework 的版本,不是一個比較通用的解法。而且空目錄判斷這樣的動作其實跟前面提到的取得目錄大小一樣,用 Win32 API 去跟 OS 問多半是最快的方式。

以這邊來說,我們可以將之改用 IsDirectoryEmplty API 去處理。

1
2
3
[ DllImport("Shlwapi.dll" , EntryPoint = "PathIsDirectoryEmpty")]
[ return: MarshalAs (UnmanagedType.Bool)]
public static extern bool IsDirectoryEmplty([MarshalAs (UnmanagedType.LPStr)] string directory);


使用起來會像下面這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System.Text;
using System.Threading.Tasks;


namespace ConsoleApplication23
{
class Program
{
[ DllImport("Shlwapi.dll" , EntryPoint = "PathIsDirectoryEmpty")]
[ return: MarshalAs (UnmanagedType.Bool)]
public static extern bool IsDirectoryEmplty([MarshalAs (UnmanagedType.LPStr)] string directory);


static void Main(string[] args)
{
var sw = Stopwatch .StartNew();
Console.WriteLine(IsDirectoryEmplty(@"C:\Program Files (x86)"));
Console.WriteLine(sw.ElapsedMilliseconds);
}
}
}


可以看到這邊判斷 Program Files 目錄也只要 1 ms 。