Last active
September 9, 2016 22:57
-
-
Save sean-m/3fef48ed479d511a4c1a to your computer and use it in GitHub Desktop.
1 file library that wraps FindFirstFileEx and FindNextFile Win32 functions to quickly enumerate the filesystem. This is an alternative to System.IO.File.GetFile and GetDirectory, this is mainly a toy exercise in performance. If you are trying to avoid exceptions in Directory.GetFiles, Marc Gravel has a great solution here: http://stackoverflow.c…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Collections.Generic; | |
using System.Runtime.InteropServices; | |
namespace NativeHelpers | |
{ | |
public static class NativeHelpers | |
{ | |
// | |
// Constants | |
// | |
const int MAX_PATH = 260; | |
const uint FILE_ATTRIBUTE_COMPRESSED = 0x800; | |
const uint FILE_ATTRIBUTE_DIRECTORY = 0x10; | |
const uint FILE_ATTRIBUTE_REPARSE_POINT = 0x400; | |
const uint ERROR_NO_MORE_FILES = 18; | |
const Int64 INVALID_HANDLE_VALUE = -1; | |
// dwAdditionalFlags: | |
const int FIND_FIRST_EX_CASE_SENSITIVE = 1; | |
const int FIND_FIRST_EX_LARGE_FETCH = 2; | |
// | |
// Win32 Data Types | |
// | |
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] | |
struct WIN32_FIND_DATA | |
{ | |
public UInt32 dwFileAttributes; | |
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime; | |
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime; | |
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime; | |
public UInt32 nFileSizeHigh; | |
public UInt32 nFileSizeLow; | |
public UInt32 dwReserved0; | |
public UInt32 dwReserved1; | |
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] | |
public string cFileName; | |
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] | |
public string cAlternateFileName; | |
} | |
enum FINDEX_INFO_LEVELS | |
{ | |
FindExInfoStandard = 0, | |
FindExInfoBasic = 1 | |
} | |
enum FINDEX_SEARCH_OPS | |
{ | |
FindExSearchNameMatch = 0, | |
FindExSearchLimitToDirectories = 1, | |
FindExSearchLimitToDevices = 2 | |
} | |
// | |
// Native Function Calls | |
// | |
[DllImport("kernel32.dll")] | |
static extern bool FindClose(IntPtr hFindFile); | |
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] | |
static extern IntPtr FindFirstFileEx( | |
string lpFileName, | |
FINDEX_INFO_LEVELS fInfoLevelId, | |
out WIN32_FIND_DATA lpFindFileData, | |
FINDEX_SEARCH_OPS fSearchOp, | |
IntPtr lpSearchFilter, | |
int dwAdditionalFlags); | |
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass"), DllImport("kernel32.dll", CharSet = CharSet.Auto)] | |
static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData); | |
// | |
// Managed Methods | |
// | |
public static string[] GetFiles(string Path, string Pattern="*.*", bool BasicInfoLevel=true) { | |
FileAttributes attr = File.GetAttributes(Path); // Also serves to check if path exists | |
var _path = ((attr & FileAttributes.Directory) == FileAttributes.Directory) ? Path : (new FileInfo(Path)).Directory.FullName; | |
_path = System.IO.Path.Combine(_path, Pattern); | |
var files = new System.Collections.Generic.List<string>(); | |
WIN32_FIND_DATA FindData; | |
FINDEX_INFO_LEVELS InfoLevel = (BasicInfoLevel) ? FINDEX_INFO_LEVELS.FindExInfoBasic : FINDEX_INFO_LEVELS.FindExInfoStandard; | |
IntPtr findHandle = FindFirstFileEx(_path, | |
InfoLevel, | |
out FindData, | |
FINDEX_SEARCH_OPS.FindExSearchNameMatch, | |
IntPtr.Zero, | |
FIND_FIRST_EX_LARGE_FETCH); | |
if (findHandle.ToInt64() != INVALID_HANDLE_VALUE) { | |
do { | |
if ( ! ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) { | |
files.Add(System.IO.Path.Combine(Path, FindData.cFileName)); | |
} | |
} while (FindNextFile(findHandle, out FindData)); | |
} | |
FindClose(findHandle); | |
return files.ToArray(); | |
} | |
public static string[] GetDirectories(string Path, string Pattern="*.*", bool BasicInfoLevel=true) { | |
FileAttributes attr = File.GetAttributes(Path); // Also serves to check if path exists | |
var _path = ((attr & FileAttributes.Directory) == FileAttributes.Directory) ? Path : (new FileInfo(Path)).Directory.FullName; | |
_path = System.IO.Path.Combine(_path, Pattern); | |
var dirs = new System.Collections.Generic.List<string>(); | |
WIN32_FIND_DATA FindData; | |
FINDEX_INFO_LEVELS InfoLevel = (BasicInfoLevel) ? FINDEX_INFO_LEVELS.FindExInfoBasic : FINDEX_INFO_LEVELS.FindExInfoStandard; | |
IntPtr findHandle = FindFirstFileEx(_path, | |
InfoLevel, | |
out FindData, | |
FINDEX_SEARCH_OPS.FindExSearchNameMatch, | |
IntPtr.Zero, | |
FIND_FIRST_EX_LARGE_FETCH); | |
if (findHandle.ToInt64() != INVALID_HANDLE_VALUE) { | |
do { | |
if ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) { | |
dirs.Add(System.IO.Path.Combine(Path,FindData.cFileName)); | |
} | |
} while (FindNextFile(findHandle, out FindData)); | |
} | |
FindClose(findHandle); | |
return dirs.ToArray(); | |
} | |
public static string[] GetFilesRecurse(string Path, string Pattern="*.*") { | |
WIN32_FIND_DATA FindData; | |
FINDEX_INFO_LEVELS InfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic; | |
IntPtr findHandle = new IntPtr(INVALID_HANDLE_VALUE); | |
var files = new List<string>(); | |
var directories = new Stack<string>(); | |
string _path; | |
FileAttributes attr = File.GetAttributes(Path); // Also serves to check if path exists | |
_path = ((attr & FileAttributes.Directory) == FileAttributes.Directory) ? Path : (new FileInfo(Path)).Directory.FullName; | |
directories.Push(System.IO.Path.GetFullPath(_path)); | |
// stack processing loop | |
while (directories.Count > 0) { | |
_path = System.IO.Path.Combine(directories.Pop(),Pattern); | |
findHandle = FindFirstFileEx(_path, | |
InfoLevel, | |
out FindData, | |
FINDEX_SEARCH_OPS.FindExSearchNameMatch, | |
IntPtr.Zero, | |
FIND_FIRST_EX_LARGE_FETCH); | |
if (findHandle.ToInt64() != INVALID_HANDLE_VALUE) { | |
do { // interate through dir contents loop | |
if (FindData.cFileName != "." && FindData.cFileName != "..") { | |
if (((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY)) { // check if it's a file | |
files.Add(System.IO.Path.Combine(_path.Replace(Pattern, ""), FindData.cFileName)); | |
} | |
else { | |
directories.Push(System.IO.Path.Combine(_path.Replace(Pattern,""), FindData.cFileName)); | |
} | |
} | |
} while (FindNextFile(findHandle, out FindData)); | |
} | |
FindClose(findHandle); | |
findHandle = new IntPtr(INVALID_HANDLE_VALUE); | |
#if DEBUG | |
Console.WriteLine("D:{0,8} F:{1,16}", directories.Count, files.Count); | |
#endif | |
} | |
return files.ToArray(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment