Skip to content

Commit

Permalink
Improving file enumeration in AppControl Manager (#470)
Browse files Browse the repository at this point in the history
Separated wildcard detection from the rest of the extensions. This improves the extension checks in the tight loops and speeds up the overall enumeration process.

Moved the default App Control supported extensions HashSet outside of the method as static var so it won't be redefined every time the method is called.
  • Loading branch information
HotCakeX authored Dec 21, 2024
1 parent 8ab8019 commit d3186c6
Showing 1 changed file with 109 additions and 37 deletions.
146 changes: 109 additions & 37 deletions AppControl Manager/Logic/GetFilesFast.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ internal static class FileUtility
};


// The Default App Control supported extensions, case-insensitive
private static readonly HashSet<string> appControlExtensions = new(StringComparer.OrdinalIgnoreCase)
{
".sys", ".exe", ".com", ".dll", ".rll", ".ocx", ".msp", ".mst", ".msi",
".js", ".vbs", ".ps1", ".appx", ".bin", ".bat", ".hxs", ".mui", ".lex", ".mof"
};


/// <summary>
/// Custom HashSet comparer to compare two FileInfo objects based on their FullName (full path of file)
/// </summary>
Expand Down Expand Up @@ -62,24 +70,21 @@ internal static List<FileInfo> GetFilesFast(
FileInfo[]? files,
string[]? extensionsToFilterBy)
{
// Create a Stopwatch instance
Stopwatch stopwatch = new();
// Create a Stopwatch instance and start measuring time
Stopwatch stopwatch = Stopwatch.StartNew();

// Start measuring time
stopwatch.Start();

// Use the Default App Control supported extensions and make them case-insensitive
HashSet<string> extensions = new(StringComparer.OrdinalIgnoreCase)
{
".sys", ".exe", ".com", ".dll", ".rll", ".ocx", ".msp", ".mst", ".msi",
".js", ".vbs", ".ps1", ".appx", ".bin", ".bat", ".hxs", ".mui", ".lex", ".mof"
};
// A HashSet used to store extensions to filter files
HashSet<string> extensions = new(StringComparer.OrdinalIgnoreCase);

// If custom extensions are provided, use them and make them case-insensitive
if (extensionsToFilterBy is { Length: > 0 })
{
extensions = new HashSet<string>(extensionsToFilterBy, StringComparer.OrdinalIgnoreCase);
}
else
{
extensions = appControlExtensions;
}

// Define a HashSet to store the final output
HashSet<FileInfo> output = new(new FileInfoComparer());
Expand All @@ -92,6 +97,9 @@ internal static List<FileInfo> GetFilesFast(
// To store all of the tasks
List<Task> tasks = [];


#region Directories

// Process directories if provided
if (directories is { Length: > 0 })
{
Expand All @@ -101,30 +109,53 @@ internal static List<FileInfo> GetFilesFast(
tasks.Add(Task.Run(() =>
{
IEnumerator<FileInfo> enumerator = directory.EnumerateFiles("*", options2).GetEnumerator();
while (true)

// If there is wildcard in extensions to filter by, then add all files without performing extension check
if (extensions.Contains("*"))
{
try
while (true)
{
// Move to the next file
if (!enumerator.MoveNext())
{
// If we reach the end of the enumeration, we break out of the loop
break;
}

// Check if the file extension is in the Extensions HashSet or Wildcard was used
if (extensions.Contains(enumerator.Current.Extension) || extensions.Contains("*"))
try
{
// Move to the next file
// The reason we use MoveNext() instead of foreach loop is that protected/inaccessible files
// Would throw errors and this way we can catch them and move to the next file without terminating the entire loop
if (!enumerator.MoveNext())
{
// If we reach the end of the enumeration, we break out of the loop
break;
}
bc.Add(enumerator.Current);
}
catch { }
}
catch { }
}
// Filter files by extensions if there is no wildcard character for filtering
else
{
while (true)
{
try
{
// Move to the next file
if (!enumerator.MoveNext())
{
// If we reach the end of the enumeration, we break out of the loop
break;
}

// Check if the file extension is in the Extensions HashSet or Wildcard was used
if (extensions.Contains(enumerator.Current.Extension))
{
bc.Add(enumerator.Current);
}
}
catch { }
}
}
}));



// Check for immediate sub-directories and process them if present
DirectoryInfo[] subDirectories = directory.GetDirectories();

Expand All @@ -136,24 +167,45 @@ internal static List<FileInfo> GetFilesFast(
tasks.Add(Task.Run(() =>
{
IEnumerator<FileInfo> subEnumerator = subDirectory.EnumerateFiles("*", options).GetEnumerator();
while (true)

if (extensions.Contains("*"))
{
try
while (true)
{
// Move to the next file
if (!subEnumerator.MoveNext())
try
{
// If we reach the end of the enumeration, we break out of the loop
break;
// Move to the next file
if (!subEnumerator.MoveNext())
{
// If we reach the end of the enumeration, we break out of the loop
break;
}
bc.Add(subEnumerator.Current);
}

// Check if the file extension is in the Extensions HashSet or Wildcard was used
if (extensions.Contains(subEnumerator.Current.Extension) || extensions.Contains("*"))
catch { }
}
}
else
{
while (true)
{
try
{
bc.Add(subEnumerator.Current);
// Move to the next file
if (!subEnumerator.MoveNext())
{
// If we reach the end of the enumeration, we break out of the loop
break;
}

// Check if the file extension is in the Extensions HashSet or Wildcard was used
if (extensions.Contains(subEnumerator.Current.Extension))
{
bc.Add(subEnumerator.Current);
}
}
catch { }
}
catch { }
}
}));
}
Expand All @@ -162,18 +214,38 @@ internal static List<FileInfo> GetFilesFast(
}
}

#endregion


#region Files

// If files are provided, process them
if (files is { Length: > 0 })
{
foreach (FileInfo file in files)
// If user provided wildcard then add all files without checking their extensions
if (extensions.Contains("*"))
{
if (extensions.Contains(file.Extension))
foreach (FileInfo file in files)
{
bc.Add(file);
}
}
// If user provided no extensions to filter by or provided extensions that are not wildcard
else
{
foreach (FileInfo file in files)
{
if (extensions.Contains(file.Extension))
{
bc.Add(file);
}
}
}
}

#endregion


// Wait for all tasks to be completed
Task.WaitAll(tasks);

Expand Down

0 comments on commit d3186c6

Please sign in to comment.