Надо сканировать огромное количество файлов и папок. Обычно для этого используется метод Directory.EnumerateFileSystemEntries.
Возможно ли как-то ускорить процесс чтения файловой системы?
Asked
Active
Viewed 802 times
7
Stack
- 9,452
2 Answers
11
Можно ускорить почти в два раза, если использовать WinAPI.
// Microsoft (R) Roslyn C# Compiler version 1.1.0.51204
using System.Runtime.InteropServices;
[DllImport("kernel32.dll")] static extern int GetLastError();
[DllImport("kernel32.dll")] static extern bool FindClose(IntPtr handle);
class SafeHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid {
private SafeHandle() : base(true) { }
protected override bool ReleaseHandle() { return FindClose(this.handle); }
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct DATA { // WIN32_FIND_DATA
public FileAttributes FileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
public uint FileSizeHigh;
public uint FileSizeLow;
public uint Reserved0;
public uint Reserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string FileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string AlternateFileName;
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern SafeHandle FindFirstFileEx(string name, int i,
out DATA data, int so, IntPtr sf, int f);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool FindNextFile(SafeHandle h, out DATA data);
IEnumerable<DATA> ReadPath(string path) { // например: "c:\" | "c:\*.png" | "c:\*"
DATA d;
var p = String.Concat(@"\\?\", path.TrimEnd('\\', ' '));
using (var h = FindFirstFileEx(p, 1, out d, 0, IntPtr.Zero, 2)) {
if (h.IsInvalid) throw new System.ComponentModel.Win32Exception(GetLastError());
yield return d;
while (FindNextFile(h, out d)) if (d.FileName != "..") yield return d;
var e = GetLastError(); // e = 18 -- дошли до конца
if (e != 18) throw new System.ComponentModel.Win32Exception(e);
}
}
Получить файлы и папки в c:\:
foreach (var d in ReadPath(@"c:\*"))
Console.WriteLine("name=" + d.FileName);
Получить файлы в c:\temp\ и подпапках:
class File { public string Path; public string Name; }
IEnumerable<File> Scan(string path) {
foreach (var d in ReadPath(path + "*").Skip(1)) {
// сканируем только подпапки, а junction пропускаем
if ((d.FileAttributes & FileAttributes.Directory) != 0
&& (d.FileAttributes & FileAttributes.ReparsePoint) == 0)
foreach (var f in Scan(path + d.FileName + "\\"))
yield return f;
else
yield return new File { Path = path, Name = d.FileName };
}
}
long ToLong(uint high, uint low) { return (long)(((UInt64)high << 0x20) | (UInt64)low); }
foreach (var f in Scan(@"c:\temp\"))
Console.WriteLine(
f.Path + "\t\t\t" + f.Name + " size=" + ToLong(d.FileSizeHigh, d.FileSizeLow));
Stack
- 9,452
2
Нужно индексировать FS в базу данных и работать потом с базой.
cpp_user
- 1,503
-
-
По хорошему драйвер на файловую систему навесить, по плохому тупо периодически обходить ее. – cpp_user Jan 13 '16 at 18:10
-
"По хорошему драйвер на файловую систему навесить" -- это как-то сложно. и не всегда возможно. "периодически обходить ее" -- чтобы обходить ее быстро надо использовать WinAPI. – Stack Jan 13 '16 at 18:22
-
Смысл был в том чтобы один раз обойти FS и через драйвер ловить все изменения FS. Никакое WinAPI такой уровень поддержки актуальности индекса и быстроты обеспечить не может. – cpp_user Jan 13 '16 at 18:27
-
Т. е. если ты будешь делать программу, которой надо искать что-то на диске, ты к ней в комплект дашь драйвер, который будет следить за всеми действиями в файловой системе? Ну круто. – Qwertiy Jan 13 '16 at 18:30
-
2Если задача состоит в том что нужно искать очень и очень быстро, а не по полчаса - то да. – cpp_user Jan 13 '16 at 18:34
-
@cpp_user "через драйвер ловить все изменения FS." -- а как это сделать? Process Monitor отлавливает все обращения к диску, но как это делается найти не смог. – Stack Jan 13 '16 at 18:41
-
Ищи по "File System Filter Driver - called on every file system I/O operation (create, read, write, rename, remove, ...)" – cpp_user Jan 13 '16 at 18:51
-
Кстати, а что будет с обновлением данных, если изменения будут внесены из другой ОС, например? – Qwertiy Jan 13 '16 at 19:39
-
Это уже административная проблема, если у вас можно внести изменения другой осью(что мешает поставить драйвер и на эту ось?) или просто молотком по железу. – cpp_user Jan 13 '16 at 19:52
[DllImport("kernel32.dll")] static extern int GetLastError();- вместо этого надо использовать Marshal.GetLastWin32Error. – Qwertiy Jan 13 '16 at 18:13Directory.EnumerateFileSystemEntriesочень много всего. посмотрите исходник – Stack Jan 13 '16 at 18:26FindFirstFileExпо идее возвращаетINVALID_HANDLE_VALUE, у вас это, кажется, приведёт к исключению. – VladD Jan 13 '16 at 21:24\\?\. – Qwertiy Jan 13 '16 at 21:33\*и не будет ошибки. а если ставите\*то значит намеревались получить содержимое, но его нет - значит fail fast. – Stack Jan 13 '16 at 21:37.и... и если..отфильтровывается, то.возвращается и для пустой папки при запросе с суффиксом\*– Stack Jan 13 '16 at 21:43.и..ей уже не соответствуют. – Qwertiy Jan 13 '16 at 22:02class File { public string Path; public string Name; }- вот тут вместо class сделать struct. – Qwertiy Jan 13 '16 at 22:05FindFirstFileExпридётся вернуть какую-нибудь ошибку. – VladD Jan 13 '16 at 22:07struct File34;30;28;27;28;39;27;28;27;27; дляclass File30;27;26;27;28;29;28;32;29;27; – Stack Jan 13 '16 at 22:42