{"id":47,"date":"2009-11-12T21:58:00","date_gmt":"2009-11-12T19:58:00","guid":{"rendered":"http:\/\/dev.bratched.fr\/en\/?p=47"},"modified":"2009-11-12T21:58:00","modified_gmt":"2009-11-12T19:58:00","slug":"playing-with-findfirstfile-and-findnextfile","status":"publish","type":"post","link":"https:\/\/bratched.com\/en\/2009\/11\/12\/playing-with-findfirstfile-and-findnextfile\/","title":{"rendered":"Playing with FindFirstFile and FindNextFile"},"content":{"rendered":"<p>Enumerating files in .NET is easy. Everybody knows the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/wz42302f.aspx\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">GetFiles<\/a> method and you may be tempted to write code like this :<\/p>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:dfd1f4f2-1d69-4d2d-89e0-7b3bceec770c\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre class=\"lang:default decode:true\">var files = Directory.GetFiles(@\"c:\\windows\\system32\", \"*.dll\");\n    foreach (var file in files)\n    {\n        Console.WriteLine(file);\n    }<\/pre>\n<\/div>\n<p>But if you look closer you will notice that this method returns an array of strings. This could be problematic if the directory you search contains lots of files. The method will block until it performs the search and once it finishes it will load all the results into memory. It would be much better if it just returned an IEnumerable. That\u2019s exactly what the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd413233(VS.100).aspx\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">EnumerateFiles<\/a> method does in .NET 4.0. Unfortunately in .NET 3.5 there\u2019s nothing for the job.<\/p>\n<p>In this post I will show how to implement this functionality using the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa364418(VS.85).aspx\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">FindFirstFile<\/a> and <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa364428(VS.85).aspx\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">FindNextFile<\/a> functions.<!--more--><\/p>\n<p>We start by defining the native function prototypes:<\/p>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:e55d321d-9622-4dc9-9557-361463671c93\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre class=\"lang:default decode:true\">internal sealed class Win32Native\n    {\n        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]\n        public struct WIN32_FIND_DATA\n        {\n            public uint dwFileAttributes;\n            public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;\n            public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;\n            public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;\n            public uint nFileSizeHigh;\n            public uint nFileSizeLow;\n            public uint dwReserved0;\n            public uint dwReserved1;\n            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]\n            public string cFileName;\n            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]\n            public string cAlternateFileName;\n        }\n\n        [DllImport(\"kernel32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern SafeFindHandle FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);\n\n        [DllImport(\"kernel32.dll\", CharSet = CharSet.Auto, SetLastError = true)]\n        public static extern bool FindNextFile(SafeFindHandle hFindFile, out WIN32_FIND_DATA lpFindFileData);\n\n        [DllImport(\"kernel32.dll\", SetLastError = true)]\n        public static extern bool FindClose(IntPtr hFindFile);\n    }<\/pre>\n<\/div>\n<p>You may notice the SafeFindHandle class used in the method signatures. This is just a simple class deriving from <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/system.runtime.interopservices.safehandle.aspx\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">SafeHandle<\/a> that will make sure that unmanaged handle is correctly closed:<\/p>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:0eb754a0-9c1b-4b2f-8362-076d8d2f0de1\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre class=\"lang:default decode:true\">[SecurityCritical]\n    internal class SafeFindHandle : SafeHandleZeroOrMinusOneIsInvalid\n    {\n        [SecurityCritical]\n        public SafeFindHandle() : base(true)\n        { }\n\n        [SecurityCritical]\n        protected override bool ReleaseHandle()\n        {\n            return Win32Native.FindClose(base.handle);\n        }\n    }<\/pre>\n<\/div>\n<p>As the documentation states the handle created by <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa364418(VS.85).aspx\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">FindFirstFile<\/a> needs to be closed with <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa364413(VS.85).aspx\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">FindClose<\/a> function. And finally the implementation of the EnumerateFiles method:<\/p>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:a9635043-3e04-4473-a1b8-35683c2a9239\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre class=\"lang:default decode:true\">public static class DirectoryExtensions\n    {\n        public static IEnumerable&lt;string&gt; EnumerateFiles(string path, string searchPattern)\n        {\n            \/\/ TODO: validate input parameters\n\n            string lpFileName = Path.Combine(path, searchPattern);\n            Win32Native.WIN32_FIND_DATA lpFindFileData;\n            var handle = Win32Native.FindFirstFile(lpFileName, out lpFindFileData);\n            if (handle.IsInvalid)\n            {\n                int hr = Marshal.GetLastWin32Error();\n                if (hr != 2 &amp;&amp; hr != 0x12)\n                {\n                    throw new Win32Exception(hr);\n                }\n                yield break;\n            }\n\n            if (IsFile(lpFindFileData))\n            {\n                var fileName = Path.Combine(path, lpFindFileData.cFileName);\n                yield return fileName;\n            }\n\n            while (Win32Native.FindNextFile(handle, out lpFindFileData))\n            {\n                if (IsFile(lpFindFileData))\n                {\n                    var fileName = Path.Combine(path, lpFindFileData.cFileName);\n                    yield return fileName;\n                }\n            }\n\n            handle.Dispose();\n        }\n\n        private static bool IsFile(Win32Native.WIN32_FIND_DATA data)\n        {\n            return 0 == (data.dwFileAttributes &amp; 0x10);\n        }\n    }<\/pre>\n<\/div>\n<p>The method could be used like this:<\/p>\n<div id=\"scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:2fffc9e3-e44c-4fa8-9b3a-7c7f33fd9707\" class=\"wlWriterSmartContent\" style=\"margin: 0px;padding: 0px;float: none\">\n<pre class=\"lang:default decode:true crayon-selected\">class Program\n    {\n        static void Main(string[] args)\n        {\n            var files = DirectoryExtensions.EnumerateFiles(@\"c:\\windows\\system32\", \"*.dll\");\n            foreach (var file in files)\n            {\n                Console.WriteLine(file);\n            }\n        }\n    }<\/pre>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Enumerating files in .NET is easy. Everybody knows the GetFiles method and you may be tempted to write code like this : var files = Directory.GetFiles(@&#8221;c:\\windows\\system32&#8243;, &#8220;*.dll&#8221;); foreach (var file in files) { Console.WriteLine(file); } But if you look closer you will notice that this method returns an array of strings. This could be problematic [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[263,266],"tags":[282,284,288,290,291,292,310],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/bratched.com\/en\/wp-json\/wp\/v2\/posts\/47"}],"collection":[{"href":"https:\/\/bratched.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bratched.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bratched.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bratched.com\/en\/wp-json\/wp\/v2\/comments?post=47"}],"version-history":[{"count":0,"href":"https:\/\/bratched.com\/en\/wp-json\/wp\/v2\/posts\/47\/revisions"}],"wp:attachment":[{"href":"https:\/\/bratched.com\/en\/wp-json\/wp\/v2\/media?parent=47"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bratched.com\/en\/wp-json\/wp\/v2\/categories?post=47"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bratched.com\/en\/wp-json\/wp\/v2\/tags?post=47"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}