写在前面
使用了Everything之后,一直对他的搜索速度感兴趣,在网上也看了很多对其原理的揭秘,终于有空找了个源码研究了一下,原理就是对NTFS的USN特性进行使用。
原理
详细解释我参照别人家的博客来一段:
当扇区的文件有变化时,操作系统会往USN Journal文件中追加一条记录,该记录包含文件名、变化发生的时间、变化的原因等信息,而不包含变化的内容。每一条记录用一个64位数字标识,称作USN(UpdateSequence Number)。微软用每一条记录在日志文件中的偏移作为该记录的USN,这样可以快速地通过USN获取到对应的记录。显而易见,USN是递增的,但是不连续。
所以如果想获得磁盘的文件只需要读取日志即可。
网上的源码有很多,关于读取日志,从日志转换成完整路径都不难理解,如果想自己写一个也是可以的。
问题
我认为需要考虑的主要问题:
- 从上次读取的位置继续读取;
- 新增或删除文件处理。
Everything 源码超级简述
源码仔细看并不难理解。
UsnOperator 类中,我认为比较重要的一点,是如何继续读取
public List<UsnEntry> GetEntries()
{var result = new List<UsnEntry>();UsnErrorCode usnErrorCode = this.QueryUSNJournal();if (usnErrorCode == UsnErrorCode.SUCCESS){MFT_ENUM_DATA mftEnumData = new MFT_ENUM_DATA();mftEnumData.StartFileReferenceNumber = 0;// 如果想从上次的位置继续读取日志// 将lowUsn修改至上次最后一个UsnEntry.Usn即可mftEnumData.LowUsn = 0;mftEnumData.HighUsn = this.ntfsUsnJournalData.NextUsn;int sizeMftEnumData = Marshal.SizeOf(mftEnumData);IntPtr ptrMftEnumData = GetHeapGlobalPtr(sizeMftEnumData);Marshal.StructureToPtr(mftEnumData, ptrMftEnumData, true);int ptrDataSize = sizeof(UInt64) + 10000;IntPtr ptrData = GetHeapGlobalPtr(ptrDataSize);uint outBytesCount;while (false != Win32Api.DeviceIoControl(this.DriveRootHandle,UsnControlCode.FSCTL_ENUM_USN_DATA,ptrMftEnumData,sizeMftEnumData,ptrData,ptrDataSize,out outBytesCount,IntPtr.Zero)){IntPtr ptrUsnRecord = new IntPtr(ptrData.ToInt32() + sizeof(Int64));while (outBytesCount > 60){var usnRecord = new USN_RECORD_V2(ptrUsnRecord);result.Add(new UsnEntry(usnRecord));ptrUsnRecord = new IntPtr(ptrUsnRecord.ToInt32() + usnRecord.RecordLength);outBytesCount -= usnRecord.RecordLength;}Marshal.WriteInt64(ptrMftEnumData, Marshal.ReadInt64(ptrData, 0));}Marshal.FreeHGlobal(ptrData);Marshal.FreeHGlobal(ptrMftEnumData);}return result;
}
使用 FileSystemWatcher 监听文件变化
关于电脑文件的修改监听,C#有相应的类来处理,非常方便,感兴趣可深挖:
FileSystemWatcher _watcher = new FileSystemWatcher(@"J:\", "*.*");
_watcher.Created += new FileSystemEventHandler(OnProcess);
_watcher.Changed += new FileSystemEventHandler(OnProcess);
_watcher.Deleted += new FileSystemEventHandler(OnProcess);
_watcher.Renamed += new RenamedEventHandler(OnFileRenamed);
_watcher.IncludeSubdirectories = true;
_watcher.EnableRaisingEvents = true;
下载
Everything相关资料下载
如果没有积分,也可以关注我获取哟~
参考资料
DeviceIOControl详解-各个击破