2012-09-07 19:08:32 +00:00
|
|
|
|
using MediaBrowser.Common.Logging;
|
|
|
|
|
using System;
|
2012-08-23 20:51:10 +00:00
|
|
|
|
using System.Collections.Generic;
|
2012-08-20 23:53:32 +00:00
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
|
|
|
|
namespace MediaBrowser.Controller.IO
|
|
|
|
|
{
|
2012-08-23 20:51:10 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Provides low level File access that is much faster than the File/Directory api's
|
|
|
|
|
/// </summary>
|
2012-08-20 23:53:32 +00:00
|
|
|
|
public static class FileData
|
|
|
|
|
{
|
|
|
|
|
public const int MAX_PATH = 260;
|
|
|
|
|
public const int MAX_ALTERNATE = 14;
|
2012-09-07 19:08:32 +00:00
|
|
|
|
public static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
|
2012-08-20 23:53:32 +00:00
|
|
|
|
|
2012-08-23 20:51:10 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets information about a path
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static WIN32_FIND_DATA GetFileData(string path)
|
2012-08-20 23:53:32 +00:00
|
|
|
|
{
|
|
|
|
|
WIN32_FIND_DATA data;
|
2012-08-23 20:51:10 +00:00
|
|
|
|
IntPtr handle = FindFirstFile(path, out data);
|
2012-09-07 19:08:32 +00:00
|
|
|
|
|
|
|
|
|
if (handle == INVALID_HANDLE_VALUE && !Path.HasExtension(path))
|
|
|
|
|
{
|
2012-09-08 14:05:09 +00:00
|
|
|
|
if (!path.EndsWith("*"))
|
|
|
|
|
{
|
|
|
|
|
Logger.LogInfo("Handle came back invalid for {0}. Since this is a directory we'll try appending \\*.", path);
|
2012-09-07 19:08:32 +00:00
|
|
|
|
|
2012-09-08 14:05:09 +00:00
|
|
|
|
FindClose(handle);
|
|
|
|
|
|
|
|
|
|
handle = FindFirstFile(Path.Combine(path, "*"), out data);
|
|
|
|
|
}
|
2012-09-07 19:08:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-08-20 23:53:32 +00:00
|
|
|
|
if (handle == IntPtr.Zero)
|
2012-09-07 19:08:32 +00:00
|
|
|
|
{
|
2012-08-20 23:53:32 +00:00
|
|
|
|
throw new IOException("FindFirstFile failed");
|
2012-09-07 19:08:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-08-20 23:53:32 +00:00
|
|
|
|
FindClose(handle);
|
2012-08-23 05:45:26 +00:00
|
|
|
|
|
2012-08-23 20:51:10 +00:00
|
|
|
|
data.Path = path;
|
2012-08-20 23:53:32 +00:00
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-23 20:51:10 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets all file system entries within a foler
|
|
|
|
|
/// </summary>
|
2012-08-23 05:45:26 +00:00
|
|
|
|
public static IEnumerable<WIN32_FIND_DATA> GetFileSystemEntries(string path, string searchPattern)
|
2012-08-23 20:51:10 +00:00
|
|
|
|
{
|
|
|
|
|
return GetFileSystemEntries(path, searchPattern, true, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets all files within a folder
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static IEnumerable<WIN32_FIND_DATA> GetFiles(string path, string searchPattern)
|
|
|
|
|
{
|
|
|
|
|
return GetFileSystemEntries(path, searchPattern, true, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets all sub-directories within a folder
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static IEnumerable<WIN32_FIND_DATA> GetDirectories(string path, string searchPattern)
|
|
|
|
|
{
|
|
|
|
|
return GetFileSystemEntries(path, searchPattern, false, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets all file system entries within a foler
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static IEnumerable<WIN32_FIND_DATA> GetFileSystemEntries(string path, string searchPattern, bool includeFiles, bool includeDirectories)
|
2012-08-23 05:45:26 +00:00
|
|
|
|
{
|
|
|
|
|
string lpFileName = Path.Combine(path, searchPattern);
|
|
|
|
|
|
|
|
|
|
WIN32_FIND_DATA lpFindFileData;
|
|
|
|
|
var handle = FindFirstFile(lpFileName, out lpFindFileData);
|
|
|
|
|
|
|
|
|
|
if (handle == IntPtr.Zero)
|
|
|
|
|
{
|
|
|
|
|
int hr = Marshal.GetLastWin32Error();
|
|
|
|
|
if (hr != 2 && hr != 0x12)
|
|
|
|
|
{
|
|
|
|
|
throw new IOException("GetFileSystemEntries failed");
|
|
|
|
|
}
|
|
|
|
|
yield break;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-23 20:51:10 +00:00
|
|
|
|
if (IncludeInOutput(lpFindFileData.cFileName, lpFindFileData.dwFileAttributes, includeFiles, includeDirectories))
|
2012-08-23 05:45:26 +00:00
|
|
|
|
{
|
|
|
|
|
yield return lpFindFileData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (FindNextFile(handle, out lpFindFileData) != IntPtr.Zero)
|
|
|
|
|
{
|
2012-08-23 20:51:10 +00:00
|
|
|
|
if (IncludeInOutput(lpFindFileData.cFileName, lpFindFileData.dwFileAttributes, includeFiles, includeDirectories))
|
2012-08-23 05:45:26 +00:00
|
|
|
|
{
|
|
|
|
|
lpFindFileData.Path = Path.Combine(path, lpFindFileData.cFileName);
|
|
|
|
|
yield return lpFindFileData;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FindClose(handle);
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-23 20:51:10 +00:00
|
|
|
|
private static bool IncludeInOutput(string cFileName, FileAttributes attributes, bool includeFiles, bool includeDirectories)
|
2012-08-23 05:45:26 +00:00
|
|
|
|
{
|
|
|
|
|
if (cFileName.Equals(".", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (cFileName.Equals("..", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-23 20:51:10 +00:00
|
|
|
|
if (!includeFiles && !attributes.HasFlag(FileAttributes.Directory))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!includeDirectories && attributes.HasFlag(FileAttributes.Directory))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-23 05:45:26 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
2012-08-20 23:53:32 +00:00
|
|
|
|
private static extern IntPtr FindFirstFile(string fileName, out WIN32_FIND_DATA data);
|
|
|
|
|
|
2012-08-23 05:45:26 +00:00
|
|
|
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
|
|
|
|
private static extern IntPtr FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA data);
|
|
|
|
|
|
2012-08-20 23:53:32 +00:00
|
|
|
|
[DllImport("kernel32")]
|
|
|
|
|
private static extern bool FindClose(IntPtr hFindFile);
|
2012-08-28 12:39:23 +00:00
|
|
|
|
|
|
|
|
|
private const char SpaceChar = ' ';
|
|
|
|
|
private static char[] InvalidFileNameChars = Path.GetInvalidFileNameChars();
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Takes a filename and removes invalid characters
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static string GetValidFilename(string filename)
|
|
|
|
|
{
|
|
|
|
|
foreach (char c in InvalidFileNameChars)
|
|
|
|
|
{
|
|
|
|
|
filename = filename.Replace(c, SpaceChar);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return filename;
|
|
|
|
|
}
|
2012-08-20 23:53:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
|
|
|
public struct FILETIME
|
|
|
|
|
{
|
|
|
|
|
public uint dwLowDateTime;
|
|
|
|
|
public uint dwHighDateTime;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
|
|
|
public struct WIN32_FIND_DATA
|
|
|
|
|
{
|
|
|
|
|
public FileAttributes dwFileAttributes;
|
|
|
|
|
public FILETIME ftCreationTime;
|
|
|
|
|
public FILETIME ftLastAccessTime;
|
|
|
|
|
public FILETIME ftLastWriteTime;
|
|
|
|
|
public int nFileSizeHigh;
|
|
|
|
|
public int nFileSizeLow;
|
|
|
|
|
public int dwReserved0;
|
|
|
|
|
public int dwReserved1;
|
|
|
|
|
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FileData.MAX_PATH)]
|
|
|
|
|
public string cFileName;
|
|
|
|
|
|
|
|
|
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = FileData.MAX_ALTERNATE)]
|
|
|
|
|
public string cAlternate;
|
|
|
|
|
|
2012-08-21 14:42:40 +00:00
|
|
|
|
public bool IsHidden
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return dwFileAttributes.HasFlag(FileAttributes.Hidden);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool IsSystemFile
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return dwFileAttributes.HasFlag(FileAttributes.System);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-20 23:53:32 +00:00
|
|
|
|
public bool IsDirectory
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return dwFileAttributes.HasFlag(FileAttributes.Directory);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-04 19:23:15 +00:00
|
|
|
|
public DateTime CreationTimeUtc
|
2012-08-20 23:53:32 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return ParseFileTime(ftCreationTime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-04 19:23:15 +00:00
|
|
|
|
public DateTime LastAccessTimeUtc
|
2012-08-20 23:53:32 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return ParseFileTime(ftLastAccessTime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-04 19:23:15 +00:00
|
|
|
|
public DateTime LastWriteTimeUtc
|
2012-08-20 23:53:32 +00:00
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return ParseFileTime(ftLastWriteTime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private DateTime ParseFileTime(FILETIME filetime)
|
|
|
|
|
{
|
|
|
|
|
long highBits = filetime.dwHighDateTime;
|
|
|
|
|
highBits = highBits << 32;
|
2012-09-04 19:23:15 +00:00
|
|
|
|
return DateTime.FromFileTimeUtc(highBits + (long)filetime.dwLowDateTime);
|
2012-08-20 23:53:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-08-21 03:56:28 +00:00
|
|
|
|
public string Path { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-20 23:53:32 +00:00
|
|
|
|
}
|