写在前面
使用了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;
下载
如果没有积分,也可以关注我获取哟~