• 在C#中快速查询文件


    相信使用过Everything的人都对其超快的搜索速度印象非常深刻,它的主要原理是通过扫描NTFS磁盘的USN Journal读取的文件列表,而不是磁盘目录,由于USN Journal非常小,因此能实现快速搜索。在CodePlex上也有人对这个功能进行了.Net的封装:MFT Scanner in VB.NET

    由于.Net程序的Dll基本上是通用的,在C#中也可以直接使用它。今天发现了有人将其翻译成了C#的版本《使用MFT Scanner遍巡USN Journal,快速找出磁碟內的所有檔案》,使用起来更为方便了。不过,原文貌似有点Bug,编译不过去,这里我改了一下,附录如下: 

      1     public class MFTScanner
      2     {
      3         private static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
      4         private const uint GENERIC_READ = 0x80000000;
      5         private const int FILE_SHARE_READ = 0x1;
      6         private const int FILE_SHARE_WRITE = 0x2;
      7         private const int OPEN_EXISTING = 3;
      8         private const int FILE_READ_ATTRIBUTES = 0x80;
      9         private const int FILE_NAME_IINFORMATION = 9;
     10         private const int FILE_FLAG_BACKUP_SEMANTICS = 0x2000000;
     11         private const int FILE_OPEN_FOR_BACKUP_INTENT = 0x4000;
     12         private const int FILE_OPEN_BY_FILE_ID = 0x2000;
     13         private const int FILE_OPEN = 0x1;
     14         private const int OBJ_CASE_INSENSITIVE = 0x40;
     15         private const int FSCTL_ENUM_USN_DATA = 0x900b3;
     16 
     17         [StructLayout(LayoutKind.Sequential)]
     18         private struct MFT_ENUM_DATA
     19         {
     20             public long StartFileReferenceNumber;
     21             public long LowUsn;
     22             public long HighUsn;
     23         }
     24 
     25         [StructLayout(LayoutKind.Sequential)]
     26         private struct USN_RECORD
     27         {
     28             public int RecordLength;
     29             public short MajorVersion;
     30             public short MinorVersion;
     31             public long FileReferenceNumber;
     32             public long ParentFileReferenceNumber;
     33             public long Usn;
     34             public long TimeStamp;
     35             public int Reason;
     36             public int SourceInfo;
     37             public int SecurityId;
     38             public FileAttributes FileAttributes;
     39             public short FileNameLength;
     40             public short FileNameOffset;
     41         }
     42 
     43         [StructLayout(LayoutKind.Sequential)]
     44         private struct IO_STATUS_BLOCK
     45         {
     46             public int Status;
     47             public int Information;
     48         }
     49 
     50         [StructLayout(LayoutKind.Sequential)]
     51         private struct UNICODE_STRING
     52         {
     53             public short Length;
     54             public short MaximumLength;
     55             public IntPtr Buffer;
     56         }
     57 
     58         [StructLayout(LayoutKind.Sequential)]
     59         private struct OBJECT_ATTRIBUTES
     60         {
     61             public int Length;
     62             public IntPtr RootDirectory;
     63             public IntPtr ObjectName;
     64             public int Attributes;
     65             public int SecurityDescriptor;
     66             public int SecurityQualityOfService;
     67         }
     68 
     69         //// MFT_ENUM_DATA
     70         [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
     71         private static extern bool DeviceIoControl(IntPtr hDevice, int dwIoControlCode, ref MFT_ENUM_DATA lpInBuffer, int nInBufferSize, IntPtr lpOutBuffer, int nOutBufferSize, ref int lpBytesReturned, IntPtr lpOverlapped);
     72 
     73         [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
     74         private static extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
     75 
     76         [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
     77         private static extern Int32 CloseHandle(IntPtr lpObject);
     78 
     79         [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
     80         private static extern int NtCreateFile(ref IntPtr FileHandle, int DesiredAccess, ref OBJECT_ATTRIBUTES ObjectAttributes, ref IO_STATUS_BLOCK IoStatusBlock, int AllocationSize, int FileAttribs, int SharedAccess, int CreationDisposition, int CreateOptions, int EaBuffer,
     81         int EaLength);
     82 
     83         [DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
     84         private static extern int NtQueryInformationFile(IntPtr FileHandle, ref IO_STATUS_BLOCK IoStatusBlock, IntPtr FileInformation, int Length, int FileInformationClass);
     85 
     86         private IntPtr m_hCJ;
     87         private IntPtr m_Buffer;
     88         private int m_BufferSize;
     89 
     90         private string m_DriveLetter;
     91 
     92         private class FSNode
     93         {
     94             public long FRN;
     95             public long ParentFRN;
     96             public string FileName;
     97 
     98             public bool IsFile;
     99             public FSNode(long lFRN, long lParentFSN, string sFileName, bool bIsFile)
    100             {
    101                 FRN = lFRN;
    102                 ParentFRN = lParentFSN;
    103                 FileName = sFileName;
    104                 IsFile = bIsFile;
    105             }
    106         }
    107 
    108         private IntPtr OpenVolume(string szDriveLetter)
    109         {
    110 
    111             IntPtr hCJ = default(IntPtr);
    112             //// volume handle
    113 
    114             m_DriveLetter = szDriveLetter;
    115             hCJ = CreateFile(@"\." + szDriveLetter, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
    116 
    117             return hCJ;
    118 
    119         }
    120 
    121 
    122         private void Cleanup()
    123         {
    124             if (m_hCJ != IntPtr.Zero)
    125             {
    126                 // Close the volume handle.
    127                 CloseHandle(m_hCJ);
    128                 m_hCJ = INVALID_HANDLE_VALUE;
    129             }
    130 
    131             if (m_Buffer != IntPtr.Zero)
    132             {
    133                 // Free the allocated memory
    134                 Marshal.FreeHGlobal(m_Buffer);
    135                 m_Buffer = IntPtr.Zero;
    136             }
    137 
    138         }
    139 
    140 
    141         public IEnumerable<String> EnumerateFiles(string szDriveLetter)
    142         {
    143             try
    144             {
    145                 var usnRecord = default(USN_RECORD);
    146                 var mft = default(MFT_ENUM_DATA);
    147                 var dwRetBytes = 0;
    148                 var cb = 0;
    149                 var dicFRNLookup = new Dictionary<long, FSNode>();
    150                 var bIsFile = false;
    151 
    152                 // This shouldn't be called more than once.
    153                 if (m_Buffer.ToInt32() != 0)
    154                 {
    155                     throw new Exception("invalid buffer");
    156                 }
    157 
    158                 // Assign buffer size
    159                 m_BufferSize = 65536;
    160                 //64KB
    161 
    162                 // Allocate a buffer to use for reading records.
    163                 m_Buffer = Marshal.AllocHGlobal(m_BufferSize);
    164 
    165                 // correct path
    166                 szDriveLetter = szDriveLetter.TrimEnd('\');
    167 
    168                 // Open the volume handle 
    169                 m_hCJ = OpenVolume(szDriveLetter);
    170 
    171                 // Check if the volume handle is valid.
    172                 if (m_hCJ == INVALID_HANDLE_VALUE)
    173                 {
    174                     string errorMsg = "Couldn't open handle to the volume.";
    175                     if (!IsAdministrator())
    176                         errorMsg += "Current user is not administrator";
    177 
    178                     throw new Exception(errorMsg);
    179                 }
    180 
    181                 mft.StartFileReferenceNumber = 0;
    182                 mft.LowUsn = 0;
    183                 mft.HighUsn = long.MaxValue;
    184 
    185                 do
    186                 {
    187                     if (DeviceIoControl(m_hCJ, FSCTL_ENUM_USN_DATA, ref mft, Marshal.SizeOf(mft), m_Buffer, m_BufferSize, ref dwRetBytes, IntPtr.Zero))
    188                     {
    189                         cb = dwRetBytes;
    190                         // Pointer to the first record
    191                         IntPtr pUsnRecord = new IntPtr(m_Buffer.ToInt32() + 8);
    192 
    193                         while ((dwRetBytes > 8))
    194                         {
    195                             // Copy pointer to USN_RECORD structure.
    196                             usnRecord = (USN_RECORD)Marshal.PtrToStructure(pUsnRecord, usnRecord.GetType());
    197 
    198                             // The filename within the USN_RECORD.
    199                             string FileName = Marshal.PtrToStringUni(new IntPtr(pUsnRecord.ToInt32() + usnRecord.FileNameOffset), usnRecord.FileNameLength / 2);
    200 
    201                             bIsFile = !usnRecord.FileAttributes.HasFlag(FileAttributes.Directory);
    202                             dicFRNLookup.Add(usnRecord.FileReferenceNumber, new FSNode(usnRecord.FileReferenceNumber, usnRecord.ParentFileReferenceNumber, FileName, bIsFile));
    203 
    204                             // Pointer to the next record in the buffer.
    205                             pUsnRecord = new IntPtr(pUsnRecord.ToInt32() + usnRecord.RecordLength);
    206 
    207                             dwRetBytes -= usnRecord.RecordLength;
    208                         }
    209 
    210                         // The first 8 bytes is always the start of the next USN.
    211                         mft.StartFileReferenceNumber = Marshal.ReadInt64(m_Buffer, 0);
    212 
    213 
    214                     }
    215                     else
    216                     {
    217                         break; // TODO: might not be correct. Was : Exit Do
    218 
    219                     }
    220 
    221                 } while (!(cb <= 8));
    222 
    223                 // Resolve all paths for Files
    224                 foreach (FSNode oFSNode in dicFRNLookup.Values.Where(o => o.IsFile))
    225                 {
    226                     string sFullPath = oFSNode.FileName;
    227                     FSNode oParentFSNode = oFSNode;
    228 
    229                     while (dicFRNLookup.TryGetValue(oParentFSNode.ParentFRN, out oParentFSNode))
    230                     {
    231                         sFullPath = string.Concat(oParentFSNode.FileName, @"", sFullPath);
    232                     }
    233                     sFullPath = string.Concat(szDriveLetter, @"", sFullPath);
    234 
    235                     yield return sFullPath;
    236                 }
    237             }
    238             finally
    239             {
    240                 //// cleanup
    241                 Cleanup();
    242             }
    243         }
    244 
    245         public static bool IsAdministrator()
    246         {
    247             WindowsIdentity identity = WindowsIdentity.GetCurrent();
    248             WindowsPrincipal principal = new WindowsPrincipal(identity);
    249             return principal.IsInRole(WindowsBuiltInRole.Administrator);
    250         }
    251     }
    View Code

    原文还提供了一个扩展方法,方便我们获取某个磁盘下的所有的文件名。 

    1     public static class DriveInfoExtension
    2     {
    3         public static IEnumerable<String> EnumerateFiles(this DriveInfo drive)
    4         {
    5             return (new MFTScanner()).EnumerateFiles(drive.Name);
    6         }
    7     }

    需要注意的是,读取USN Journal是需要管理员权限的,因此使用这个类需要管理员权限才能正常运行。

    另外,这个类封装的也略为简单,只读取了文件名,实际上还可以读取文件大小,属性等常用信息,修改一下代码非常容易获取这些属性。通过它们可以非常方便写出一些分析磁盘空间占用的程序,这里就不举例了。

  • 相关阅读:
    最有价值的专家--微软官方网站
    CC++初学者编程教程(16) 搭建Xcode cocos2dx开发环境--尹成老师博客
    微软最具价值的专家之毛星云博客
    Winsock IO模型之IOCP模型
    IOCP模型与网络编程
    关于proc索引的创建
    查找表存在于那些proc中
    Sql 时间做条件
    Sql 无指定条件,防并发update
    sql 根据客户需用 增减查询字段
  • 原文地址:https://www.cnblogs.com/TianFang/p/3427776.html
Copyright © 2020-2023  润新知