Skip to content

Instantly share code, notes, and snippets.

@sean-m
Last active September 9, 2016 22:57
Show Gist options
  • Save sean-m/3fef48ed479d511a4c1a to your computer and use it in GitHub Desktop.
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…
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