// This is based on MSDN Magazine article codes by Stephen Toub (stoub@microsoft.com) // The article is http://msdn.microsoft.com/en-us/magazine/cc163696.aspx using System; using System.IO; using System.Security; using System.ComponentModel; using System.Collections.Generic; using System.Security.Permissions; using Microsoft.Win32.SafeHandles; using System.Runtime.InteropServices; using System.Runtime.ConstrainedExecution; using ComTypes = System.Runtime.InteropServices.ComTypes; namespace SharpVectors.Converters.Utils { internal static class DirectoryUtils { private sealed class SafeFindHandle : SafeHandleZeroOrMinusOneIsInvalid { [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] private SafeFindHandle() : base(true) { } protected override bool ReleaseHandle() { return FindClose(this.handle); } [DllImport("kernel32.dll")] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] [SuppressUnmanagedCodeSecurity] private static extern bool FindClose(IntPtr handle); } [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] [SuppressUnmanagedCodeSecurity] private static extern SafeFindHandle FindFirstFile(string lpFileName, [In, Out, MarshalAs(UnmanagedType.LPStruct)] WIN32_FIND_DATA lpFindFileData); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] [SuppressUnmanagedCodeSecurity] private static extern bool FindNextFile(SafeFindHandle hndFindFile, [In, Out, MarshalAs(UnmanagedType.LPStruct)] WIN32_FIND_DATA lpFindFileData); [DllImport("kernel32.dll", SetLastError = true)] private static extern ErrorModes SetErrorMode(ErrorModes newMode); [Serializable] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] [BestFitMapping(false)] private class WIN32_FIND_DATA { public FileAttributes dwFileAttributes; public ComTypes.FILETIME ftCreationTime; public ComTypes.FILETIME ftLastAccessTime; public ComTypes.FILETIME ftLastWriteTime; public int nFileSizeHigh; public int nFileSizeLow; public int dwReserved0; public int dwReserved1; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string cFileName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] public string cAlternateFileName; } private const int ERROR_FILE_NOT_FOUND = 0x2; private const int ERROR_ACCESS_DENIED = 0x5; private const int ERROR_NO_MORE_FILES = 0x12; [Flags] private enum ErrorModes { /// Use the system default, which is to display all error dialog boxes. Default = 0x0, /// /// The system does not display the critical-error-handler message box. /// Instead, the system sends the error to the calling process. /// FailCriticalErrors = 0x1, /// /// 64-bit Windows: The system automatically fixes memory alignment faults and makes them /// invisible to the application. It does this for the calling process and any descendant processes. /// After this value is set for a process, subsequent attempts to clear the value are ignored. /// NoGpFaultErrorBox = 0x2, /// /// The system does not display the general-protection-fault message box. /// This flag should only be set by debugging applications that handle general /// protection (GP) faults themselves with an exception handler. /// NoAlignmentFaultExcept = 0x4, /// /// The system does not display a message box when it fails to find a file. /// Instead, the error is returned to the calling process. /// NoOpenFileErrorBox = 0x8000 } public static void DeleteDirectory(string directoryPath, bool recursive) { if (String.IsNullOrEmpty(directoryPath)) { return; } DirectoryInfo dirInfo = new DirectoryInfo(directoryPath); if (dirInfo.Exists) { // It is a directory... try { dirInfo.Attributes = FileAttributes.Normal; dirInfo.Delete(recursive); } catch (UnauthorizedAccessException) { // One possible cause of this is read-only file, so first // try another method of deleting the directory... foreach (string file in DirectoryUtils.FindFiles(dirInfo, "*.*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)) { File.SetAttributes(file, FileAttributes.Normal); File.Delete(file); } dirInfo.Delete(recursive); } } } public static IEnumerable FindFiles(DirectoryInfo dir, string pattern, SearchOption searchOption) { // We suppressed this demand for each p/invoke call, so demand it upfront once new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); // Validate parameters if (dir == null) throw new ArgumentNullException("dir"); if (pattern == null) throw new ArgumentNullException("pattern"); // Setup WIN32_FIND_DATA findData = new WIN32_FIND_DATA(); Stack directories = new Stack(); directories.Push(dir); // Process each directory ErrorModes origErrorMode = SetErrorMode(ErrorModes.FailCriticalErrors); try { while (directories.Count > 0) { // Get the name of the next directory and the corresponding search pattern dir = directories.Pop(); string dirPath = dir.FullName.Trim(); if (dirPath.Length == 0) continue; char lastChar = dirPath[dirPath.Length - 1]; if (lastChar != Path.DirectorySeparatorChar && lastChar != Path.AltDirectorySeparatorChar) { dirPath += Path.DirectorySeparatorChar; } // Process all files in that directory SafeFindHandle handle = FindFirstFile(dirPath + pattern, findData); if (handle.IsInvalid) { int error = Marshal.GetLastWin32Error(); if (error == ERROR_ACCESS_DENIED || error == ERROR_FILE_NOT_FOUND) { continue; } else { throw new Win32Exception(error); } } else { try { do { if ((findData.dwFileAttributes & FileAttributes.Directory) == 0) yield return (dirPath + findData.cFileName); } while (FindNextFile(handle, findData)); int error = Marshal.GetLastWin32Error(); if (error != ERROR_NO_MORE_FILES) { throw new Win32Exception(error); } } finally { handle.Dispose(); } } // Add all child directories if that's what the user wants if (searchOption == SearchOption.AllDirectories) { foreach (DirectoryInfo childDir in dir.GetDirectories()) { if ((File.GetAttributes(childDir.FullName) & FileAttributes.ReparsePoint) == 0) { directories.Push(childDir); } } } } } finally { SetErrorMode(origErrorMode); } } } }