#c# #winapi
Вопрос:
Ниже приведено консольное приложение в c#.net показывает только имена папок на диске D без полного пути , но я хочу рекурсивно повторять внутри папок, вложенных папок и файлов в этих вложенных папках с помощью win32 API, потому что мне нужно сканировать папки, содержащие тысячи файлов. Win32 API работает быстро, поэтому я не использую обычные c#.net функция для получения каталогов и их файлов.
class Program
{
// Kernal32.dll import for File management operations.
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr FindFirstFileEx(
string lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
out WIN32_FIND_DATA lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
IntPtr lpSearchFilter,
int dwAdditionalFlags);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32.dll")]
static extern bool FindClose(IntPtr hFindFile);
public const int FIND_FIRST_EX_CASE_SENSITIVE = 1;
public const int FIND_FIRST_EX_LARGE_FETCH = 2;
static void Main()
{
WIN32_FIND_DATA findData;
FINDEX_INFO_LEVELS findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;
int additionalFlags = 0;
if (Environment.OSVersion.Version.Major >= 6)
{
findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;
//additionalFlags = FIND_FIRST_EX_LARGE_FETCH;
}
string pattern = "D:\*.*";
IntPtr hFile = FindFirstFileEx(
pattern,
findInfoLevel,
out findData,
FINDEX_SEARCH_OPS.FindExSearchNameMatch,
IntPtr.Zero,
additionalFlags);
int error = Marshal.GetLastWin32Error();
if (hFile.ToInt32() != -1)
{
do
{
Console.WriteLine("Found file {0}, amp; Attribute is {1}", findData.cFileName, findData.dwFileAttributes);
}
while (FindNextFile(hFile, out findData));
FindClose(hFile);
}
Console.ReadLine();
}
}
Ниже приведены необходимые перечисления и структура для программы.
public enum FINDEX_SEARCH_OPS
{
FindExSearchNameMatch = 0,
FindExSearchLimitToDirectories = 1,
FindExSearchLimitToDevices = 2
}
public enum FINDEX_INFO_LEVELS
{
FindExInfoStandard = 0,
FindExInfoBasic = 1
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct WIN32_FIND_DATA
{
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
I have got code from Pinvoke.net and website is http://pinvoke.net/
Комментарии:
1. «Win32 API работает быстро, поэтому я не использую обычные c#.net функция для получения каталогов и их файлов.» Вы получили какие-либо тесты? Разве это не должно быть узким местом из-за скорости чтения с диска, а не кода?
2. Вы пробовали Стандартную . Сеть с перечислением вместо списка? Насколько мне известно, эти функции выполняются намного быстрее.
3. @cid да, я пытался standard.net библиотека , у нее много накладных расходов, поэтому win32 удобен и быстр
4. @DanielW да, я пытался standard.net библиотека , у нее много накладных расходов, поэтому win32 удобен и быстр
5. Трудно поверить, что p/invoke будет быстрее, чем стандартная библиотека
Ответ №1:
Вы можете поместить свой код в рекурсивную функцию и вызвать ее для каждой папки:
static void Main()
{
WIN32_FIND_DATA findData;
FINDEX_INFO_LEVELS findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;
int additionalFlags = 0;
if (Environment.OSVersion.Version.Major >= 6)
{
findInfoLevel = FINDEX_INFO_LEVELS.FindExInfoBasic;
//additionalFlags = FIND_FIRST_EX_LARGE_FETCH;
}
string startFolder = @"D:";
var watch = new Stopwatch();
watch.Start();
IterateFolder(findInfoLevel, additionalFlags, startFolder);
watch.Stop();
Console.WriteLine();
Console.WriteLine($"{watch.Elapsed.TotalSeconds}");
Console.ReadLine();
}
public const uint FILE_ATTRIBUTE_DIRECTORY = 0x10;
private static void IterateFolder(FINDEX_INFO_LEVELS findInfoLevel, int additionalFlags, string folder)
{
WIN32_FIND_DATA findData;
IntPtr hFile = FindFirstFileEx(
$"{folder}\*.*",
findInfoLevel,
out findData,
FINDEX_SEARCH_OPS.FindExSearchNameMatch,
IntPtr.Zero,
additionalFlags);
int error = Marshal.GetLastWin32Error();
if (hFile.ToInt32() != -1)
{
do
{
if (findData.cFileName == "." || findData.cFileName == "..") continue;//ignore folder navigation
var foundFolder = $"{folder}\{findData.cFileName}";
Console.WriteLine("Found file {0}, amp; Attribute is {1}", foundFolder, findData.dwFileAttributes);
if ((findData.dwFileAttributes amp; FILE_ATTRIBUTE_DIRECTORY) > 0)
{
//Recursive for directories
IterateFolder(findInfoLevel, additionalFlags, foundFolder);
}
}
while (FindNextFile(hFile, out findData));
FindClose(hFile);
}
}
Комментарии:
1. Я думаю, что вы сделали многое в основном, я отредактировал сообщение и поместил в него полный основной текст. Кстати. похоже, вы правы, что WinAPI примерно на 20% быстрее, чем pure .Net.
2. сэр, вы правы !! Большое спасибо !! Сэр , где я могу получить помощь, связанную с программированием win32, в сети мало что доступно, и я не мог понять MSND, так как он просто дает подпись встроенной функции.