using MediaBrowser.Common.Win32; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace MediaBrowser.Common.IO { /// /// Class FileSystem /// public static class FileSystem { /// /// Gets information about a path /// /// The path. /// System.Nullable{WIN32_FIND_DATA}. /// path /// GetFileData failed for + path public static WIN32_FIND_DATA? GetFileData(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } WIN32_FIND_DATA data; var handle = NativeMethods.FindFirstFileEx(path, FINDEX_INFO_LEVELS.FindExInfoBasic, out data, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, FindFirstFileExFlags.NONE); var getFilename = false; if (handle == NativeMethods.INVALID_HANDLE_VALUE && !Path.HasExtension(path)) { if (!path.EndsWith("*", StringComparison.OrdinalIgnoreCase)) { NativeMethods.FindClose(handle); handle = NativeMethods.FindFirstFileEx(Path.Combine(path, "*"), FINDEX_INFO_LEVELS.FindExInfoBasic, out data, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, FindFirstFileExFlags.NONE); getFilename = true; } } if (handle == IntPtr.Zero) { throw new IOException("GetFileData failed for " + path); } NativeMethods.FindClose(handle); // According to MSDN documentation, this will default to 1601 for paths that don't exist. if (data.CreationTimeUtc.Year == 1601) { return null; } if (getFilename) { data.cFileName = Path.GetFileName(path); } data.Path = path; return data; } /// /// Gets all files within a folder /// /// The path. /// The search pattern. /// IEnumerable{WIN32_FIND_DATA}. public static IEnumerable GetFiles(string path, string searchPattern = "*") { return GetFileSystemEntries(path, searchPattern, includeDirectories: false); } /// /// Gets all sub-directories within a folder /// /// The path. /// IEnumerable{WIN32_FIND_DATA}. public static IEnumerable GetDirectories(string path) { return GetFileSystemEntries(path, includeFiles: false); } /// /// Gets all file system entries within a foler /// /// The path. /// The search pattern. /// if set to true [include files]. /// if set to true [include directories]. /// IEnumerable{WIN32_FIND_DATA}. /// path /// GetFileSystemEntries failed public static IEnumerable GetFileSystemEntries(string path, string searchPattern = "*", bool includeFiles = true, bool includeDirectories = true) { if (string.IsNullOrEmpty(path)) { throw new ArgumentNullException("path"); } var lpFileName = Path.Combine(path, searchPattern); WIN32_FIND_DATA lpFindFileData; var handle = NativeMethods.FindFirstFileEx(lpFileName, FINDEX_INFO_LEVELS.FindExInfoBasic, out lpFindFileData, FINDEX_SEARCH_OPS.FindExSearchNameMatch, IntPtr.Zero, FindFirstFileExFlags.FIND_FIRST_EX_LARGE_FETCH); if (handle == IntPtr.Zero) { var hr = Marshal.GetLastWin32Error(); if (hr != 2 && hr != 0x12) { throw new IOException("GetFileSystemEntries failed"); } yield break; } if (IncludeInFindFileOutput(lpFindFileData.cFileName, lpFindFileData.dwFileAttributes, includeFiles, includeDirectories)) { lpFindFileData.Path = Path.Combine(path, lpFindFileData.cFileName); yield return lpFindFileData; } while (NativeMethods.FindNextFile(handle, out lpFindFileData) != IntPtr.Zero) { if (IncludeInFindFileOutput(lpFindFileData.cFileName, lpFindFileData.dwFileAttributes, includeFiles, includeDirectories)) { lpFindFileData.Path = Path.Combine(path, lpFindFileData.cFileName); yield return lpFindFileData; } } NativeMethods.FindClose(handle); } /// /// Includes the in find file output. /// /// Name of the c file. /// The attributes. /// if set to true [include files]. /// if set to true [include directories]. /// true if XXXX, false otherwise public static bool IncludeInFindFileOutput(string cFileName, FileAttributes attributes, bool includeFiles, bool includeDirectories) { if (cFileName.Equals(".", StringComparison.OrdinalIgnoreCase)) { return false; } if (cFileName.Equals("..", StringComparison.OrdinalIgnoreCase)) { return false; } if (!includeFiles && !attributes.HasFlag(FileAttributes.Directory)) { return false; } if (!includeDirectories && attributes.HasFlag(FileAttributes.Directory)) { return false; } return true; } /// /// The space char /// private const char SpaceChar = ' '; /// /// The invalid file name chars /// private static readonly char[] InvalidFileNameChars = Path.GetInvalidFileNameChars(); /// /// Takes a filename and removes invalid characters /// /// The filename. /// System.String. /// filename public static string GetValidFilename(string filename) { if (string.IsNullOrEmpty(filename)) { throw new ArgumentNullException("filename"); } foreach (var c in InvalidFileNameChars) { filename = filename.Replace(c, SpaceChar); } return filename; } /// /// Resolves the shortcut. /// /// The filename. /// System.String. /// filename public static string ResolveShortcut(string filename) { if (string.IsNullOrEmpty(filename)) { throw new ArgumentNullException("filename"); } var link = new ShellLink(); ((IPersistFile)link).Load(filename, NativeMethods.STGM_READ); // TODO: if I can get hold of the hwnd call resolve first. This handles moved and renamed files. // ((IShellLinkW)link).Resolve(hwnd, 0) var sb = new StringBuilder(NativeMethods.MAX_PATH); WIN32_FIND_DATA data; ((IShellLinkW)link).GetPath(sb, sb.Capacity, out data, 0); return sb.ToString(); } /// /// Creates a shortcut file pointing to a specified path /// /// The shortcut path. /// The target. /// shortcutPath public static void CreateShortcut(string shortcutPath, string target) { if (string.IsNullOrEmpty(shortcutPath)) { throw new ArgumentNullException("shortcutPath"); } if (string.IsNullOrEmpty(target)) { throw new ArgumentNullException("target"); } var link = new ShellLink(); ((IShellLinkW)link).SetPath(target); ((IPersistFile)link).Save(shortcutPath, true); } /// /// Determines whether the specified filename is shortcut. /// /// The filename. /// true if the specified filename is shortcut; otherwise, false. /// filename public static bool IsShortcut(string filename) { if (string.IsNullOrEmpty(filename)) { throw new ArgumentNullException("filename"); } return string.Equals(Path.GetExtension(filename), ".lnk", StringComparison.OrdinalIgnoreCase); } /// /// Copies all. /// /// The source. /// The target. /// source /// The source and target directories are the same public static void CopyAll(string source, string target) { if (string.IsNullOrEmpty(source)) { throw new ArgumentNullException("source"); } if (string.IsNullOrEmpty(target)) { throw new ArgumentNullException("target"); } if (source.Equals(target, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("The source and target directories are the same"); } // Check if the target directory exists, if not, create it. if (!Directory.Exists(target)) { Directory.CreateDirectory(target); } foreach (var file in Directory.EnumerateFiles(source)) { File.Copy(file, Path.Combine(target, Path.GetFileName(file)), true); } // Copy each subdirectory using recursion. foreach (var dir in Directory.EnumerateDirectories(source)) { CopyAll(dir, Path.Combine(target, Path.GetFileName(dir))); } } /// /// Parses the ini file. /// /// The path. /// NameValueCollection. public static NameValueCollection ParseIniFile(string path) { var values = new NameValueCollection(); foreach (var line in File.ReadAllLines(path)) { var data = line.Split('='); if (data.Length < 2) continue; var key = data[0]; var value = data.Length == 2 ? data[1] : string.Join(string.Empty, data, 1, data.Length - 1); values[key] = value; } return values; } } }