在C#中快速查询文件

摘要:
相信使用过Everything的人对其超快的搜索速度印象深刻。它的主要原理是扫描USNJournal在NTFS磁盘上读取的文件列表,而不是磁盘目录。因为USNJournal很小,所以可以实现快速搜索。CodePlex上的人也将此函数封装在中。Net:MFTS扫描程序VB.Net。因为Net程序的Dll基本上是通用的,也可以直接在C#中使用。今天,我发现有人将其翻译成C#版本,“使用MFTScanner浏览USNJournal并快速查找磁盘上的所有文件”,这更方便使用。

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

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

1     public classMFTScanner
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 structMFT_ENUM_DATA
19 {
20             public longStartFileReferenceNumber;
21             public longLowUsn;
22             public longHighUsn;
23 }
24 
25 [StructLayout(LayoutKind.Sequential)]
26         private structUSN_RECORD
27 {
28             public intRecordLength;
29             public shortMajorVersion;
30             public shortMinorVersion;
31             public longFileReferenceNumber;
32             public longParentFileReferenceNumber;
33             public longUsn;
34             public longTimeStamp;
35             public intReason;
36             public intSourceInfo;
37             public intSecurityId;
38             publicFileAttributes FileAttributes;
39             public shortFileNameLength;
40             public shortFileNameOffset;
41 }
42 
43 [StructLayout(LayoutKind.Sequential)]
44         private structIO_STATUS_BLOCK
45 {
46             public intStatus;
47             public intInformation;
48 }
49 
50 [StructLayout(LayoutKind.Sequential)]
51         private structUNICODE_STRING
52 {
53             public shortLength;
54             public shortMaximumLength;
55             publicIntPtr Buffer;
56 }
57 
58 [StructLayout(LayoutKind.Sequential)]
59         private structOBJECT_ATTRIBUTES
60 {
61             public intLength;
62             publicIntPtr RootDirectory;
63             publicIntPtr ObjectName;
64             public intAttributes;
65             public intSecurityDescriptor;
66             public intSecurityQualityOfService;
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 intlpBytesReturned, 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, intdwFlagsAndAttributes, IntPtr hTemplateFile);
75 
76         [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet =CharSet.Auto)]
77         private static externInt32 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, intEaBuffer,
81         intEaLength);
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, intFileInformationClass);
85 
86         privateIntPtr m_hCJ;
87         privateIntPtr m_Buffer;
88         private intm_BufferSize;
89 
90         private stringm_DriveLetter;
91 
92         private classFSNode
93 {
94             public longFRN;
95             public longParentFRN;
96             public stringFileName;
97 
98             public boolIsFile;
99             public FSNode(long lFRN, long lParentFSN, string sFileName, boolbIsFile)
100 {
101                 FRN =lFRN;
102                 ParentFRN =lParentFSN;
103                 FileName =sFileName;
104                 IsFile =bIsFile;
105 }
106 }
107 
108         private IntPtr OpenVolume(stringszDriveLetter)
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             returnhCJ;
118 
119 }
120 
121 
122         private voidCleanup()
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(stringszDriveLetter)
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 newException(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, refdwRetBytes, 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, newFSNode(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, outoParentFSNode))
230 {
231                         sFullPath = string.Concat(oParentFSNode.FileName, @"", sFullPath);
232 }
233                     sFullPath = string.Concat(szDriveLetter, @"", sFullPath);
234 
235                     yield returnsFullPath;
236 }
237 }
238             finally
239 {
240                 //// cleanup
241 Cleanup();
242 }
243 }
244 
245         public static boolIsAdministrator()
246 {
247             WindowsIdentity identity =WindowsIdentity.GetCurrent();
248             WindowsPrincipal principal = newWindowsPrincipal(identity);
249             returnprincipal.IsInRole(WindowsBuiltInRole.Administrator);
250 }
251     }
View Code

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

1     public static classDriveInfoExtension
2 {
3         public static IEnumerable<String> EnumerateFiles(thisDriveInfo drive)
4 {
5             return (newMFTScanner()).EnumerateFiles(drive.Name);
6 }
7     }

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

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

免责声明:文章转载自《在C#中快速查询文件》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇VMware安装的Linux系统忘记密码 怎么修改root密码python psycopg2 连接pg 建立连接池下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

控制Windows音量

public partial class AdjustVolume { [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern IntPtr SendMessage(IntPtr hWnd, uint M...

【C++ Primer 第13章】6.对象移动

右值引用 左值和右值 (1)两者区别:   ①左值:能对表达式取地址、或具名对象/变量。一般指表达式结束后依然存在的持久对象。   ②右值:不能对表达式取地址,或匿名对象。一般指表达式结束就不再存在的临时对象。 总结:一般而言,一个左值表达式表示的是一个对象的身份,而一个右值表达式表示的是对象的值。  (2)右值的分类   ①将亡值(xvalue,eXpi...

winform窗体 小程序【移动窗体和阴影】

窗体无边框设置后无法移动,引用API 使其获得功能 移动 //窗体移动API [DllImport("user32.dll")] public static extern bool ReleaseCapture(); [DllImport("user32.dll")] public static extern bool SendMessage(IntPt...

图片在 canvas 中的 选中/平移/缩放/旋转,包含了所有canvas的2D变化,让你认识到数学的重要性

1、介绍   canvas 已经出来好久了,相信大家多少都有接触。   如果你是前端页面开发/移动开发,那么你肯定会有做过图片上传处理,图片优化,以及图片合成,这些都是可以用 canvas 实现的。   如果你是做前端游戏开发的,可能会非常熟悉,或者说对几何和各种图形变化非常了解。   这里我介绍的是简单的、基本的,但是非常完全的一个 2d 的 canva...

duilib之源码分析

《duilib之源码分析》1 stdAfx.h [cpp]view plaincopy *lengthof(x)计算数组长度宏 *MAX两值最大 *MIN两值最小 *CLAMP(x,a,b)x在a,b之间则取x否则取最小 2 UIBase [cpp]view plaincopy *UI_WNDSTYLE_窗口风格属性 *UI_CLASS...

10分钟学会React Context API

Create-react-app来学习这个功能: 注意下面代码红色的即可,非常简单. 在小项目里Context API完全可以替换掉react-redux. 修改app.js import React, { lazy, useState } from "react"; import { Button } from 'antd'; import { Hash...