Как получить список каталогов и файлов с полным путем с помощью win32 в приложении c#

#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, так как он просто дает подпись встроенной функции.