private static bool FirstFileExists(FastIOSearchOption searchOption, string path, params string[] searchPatterns) { path = path.TrimEnd('\\'); if (string.IsNullOrWhiteSpace(path) || Path.GetInvalidPathChars().Any(path.Contains)) { throw new ArgumentException("The path '" + path + "' is invalid in some, or other, regard."); } const int fileAttributeDirectory = 0x10; const int ERROR_FILE_NOT_FOUND = 0x2; // Other relevant errors (though we don't use them specifically at the moment) //const int ERROR_PATH_NOT_FOUND = 0x3; //const int ERROR_REM_NOT_LIST = 0x33; //const int ERROR_BAD_NETPATH = 0x35; WIN32_FIND_DATAW findData; // Search the base directory first, and only then search subdirectories. // TODO: Fix goofy duplicate code if (searchOption != FastIOSearchOption.AllDirectoriesSkipTop) { foreach (var p in searchPatterns) { using var findHandle = FindFirstFileW(@"\\?\" + path.TrimEnd('\\') + '\\' + p, out findData); if (findHandle.IsInvalid) { var err = Marshal.GetLastWin32Error(); if (err == ERROR_FILE_NOT_FOUND) { continue; } // Since the framework isn't here to save us, we should blanket-catch and throw on every // possible error other than file-not-found (as that's an intended scenario, obviously). // This isn't as nice as you'd get from a framework method call, but it gets the job done. ThrowException(searchPatterns, err, path, p, 0); } do { if ((findData.dwFileAttributes & fileAttributeDirectory) != fileAttributeDirectory && findData.cFileName != "." && findData.cFileName != "..") { return(true); } } while (FindNextFileW(findHandle, out findData)); if (searchOption == FastIOSearchOption.TopDirectoryOnly) { return(false); } } } using (var findHandle = FindFirstFileW(@"\\?\" + path.TrimEnd('\\') + @"\*", out findData)) { if (findHandle.IsInvalid) { var err = Marshal.GetLastWin32Error(); if (err != ERROR_FILE_NOT_FOUND) { ThrowException(searchPatterns, err, path, @"\* [looking for all directories]", 1); } } do { if ((findData.dwFileAttributes & fileAttributeDirectory) == fileAttributeDirectory && findData.cFileName != "." && findData.cFileName != ".." && FirstFileExists(FastIOSearchOption.AllDirectories, Path.Combine(path, findData.cFileName), searchPatterns)) { return(true); } } while (FindNextFileW(findHandle, out findData)); return(false); } }
private static bool FirstFileExists(FastIOSearchOption searchOption, string path, params string[] searchPatterns) { path = NormalizeAndCheckPath(path, pathIsKnownValid: false); // Other relevant errors (though we don't use them specifically at the moment) //const int ERROR_PATH_NOT_FOUND = 0x3; //const int ERROR_REM_NOT_LIST = 0x33; //const int ERROR_BAD_NETPATH = 0x35; WIN32_FIND_DATAW findData; // Search the base directory first, and only then search subdirectories. string pathC = @"\\?\" + path + "\\"; foreach (string p in searchPatterns) { using SafeSearchHandle findHandle = FindFirstFileExW( pathC + p, FINDEX_INFO_LEVELS.FindExInfoBasic, out findData, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, 0); if (findHandle.IsInvalid) { int err = Marshal.GetLastWin32Error(); if (err == ERROR_FILE_NOT_FOUND) { continue; } // Since the framework isn't here to save us, we should blanket-catch and throw on every // possible error other than file-not-found (as that's an intended scenario, obviously). // This isn't as nice as you'd get from a framework method call, but it gets the job done. ThrowException(searchPatterns, err, path, p, 0); } do { if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY && findData.cFileName != "." && findData.cFileName != "..") { return(true); } } while (FindNextFileW(findHandle, out findData)); if (searchOption == FastIOSearchOption.TopDirectoryOnly) { return(false); } } using (SafeSearchHandle findHandle = FindFirstFileExW( pathC + "*", FINDEX_INFO_LEVELS.FindExInfoBasic, out findData, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, 0)) { if (findHandle.IsInvalid) { int err = Marshal.GetLastWin32Error(); if (err != ERROR_FILE_NOT_FOUND) { ThrowException(searchPatterns, err, path, @"\* [looking for all directories]", 1); } } do { if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY && findData.cFileName != "." && findData.cFileName != ".." && FirstFileExists(FastIOSearchOption.AllDirectories, Path.Combine(path, findData.cFileName), searchPatterns)) { return(true); } } while (FindNextFileW(findHandle, out findData)); return(false); } }
private static bool FirstFileExists(FastIOSearchOption searchOption, string path, params string[] searchPatterns) { // Vital, path must not have a trailing separator // We also normalize manually to all backslashes because we use \\?\ which skips normalization path = path.Replace('/', '\\').TrimEnd(FMConstants.CA_Backslash); bool pathContainsInvalidChars = false; char[] invalidChars = Path.GetInvalidPathChars(); // Dumb loop to avoid LINQ. for (int i = 0; i < invalidChars.Length; i++) { if (path.Contains(invalidChars[i])) { pathContainsInvalidChars = true; break; } } if (path.IsWhiteSpace() || pathContainsInvalidChars) { throw new ArgumentException("The path '" + path + "' is invalid in some, or other, regard."); } const int FILE_ATTRIBUTE_DIRECTORY = 0x10; const int ERROR_FILE_NOT_FOUND = 0x2; // Other relevant errors (though we don't use them specifically at the moment) //const int ERROR_PATH_NOT_FOUND = 0x3; //const int ERROR_REM_NOT_LIST = 0x33; //const int ERROR_BAD_NETPATH = 0x35; WIN32_FIND_DATAW findData; // Search the base directory first, and only then search subdirectories. // TODO: Fix goofy duplicate code string pathC = @"\\?\" + path + "\\"; if (searchOption != FastIOSearchOption.AllDirectoriesSkipTop) { foreach (string p in searchPatterns) { using SafeSearchHandle findHandle = FindFirstFileEx( pathC + p, FINDEX_INFO_LEVELS.FindExInfoBasic, out findData, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, 0); if (findHandle.IsInvalid) { int err = Marshal.GetLastWin32Error(); if (err == ERROR_FILE_NOT_FOUND) { continue; } // Since the framework isn't here to save us, we should blanket-catch and throw on every // possible error other than file-not-found (as that's an intended scenario, obviously). // This isn't as nice as you'd get from a framework method call, but it gets the job done. ThrowException(searchPatterns, err, path, p, 0); } do { if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY && findData.cFileName != "." && findData.cFileName != "..") { return(true); } } while (FindNextFileW(findHandle, out findData)); if (searchOption == FastIOSearchOption.TopDirectoryOnly) { return(false); } } } using (SafeSearchHandle findHandle = FindFirstFileEx( pathC + "*", FINDEX_INFO_LEVELS.FindExInfoBasic, out findData, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, 0)) { if (findHandle.IsInvalid) { int err = Marshal.GetLastWin32Error(); if (err != ERROR_FILE_NOT_FOUND) { ThrowException(searchPatterns, err, path, @"\* [looking for all directories]", 1); } } do { if ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY && findData.cFileName != "." && findData.cFileName != ".." && FirstFileExists(FastIOSearchOption.AllDirectories, Path.Combine(path, findData.cFileName), searchPatterns)) { return(true); } } while (FindNextFileW(findHandle, out findData)); return(false); } }