[转载]Delphi 版 everything、光速搜索代码

近日没啥事情,研究了一下 everything、光速搜索原理。花了一个礼拜时间,终于搞定。

废话不多说,直接上代码:

[delphi] view plain copy
 
  1. unit uMFTSearchFile;  
  2. { 
  3.   dbyoung@sina.com 
  4.   2018-04-23 
  5. }  
  6.   
  7. interface  
  8.   
  9. uses Windows, System.Classes, Generics.Collections;  
  10.   
  11. { 获取磁盘所有文件列表 }  
  12. function GetLogicalDiskAllFiles(const chrLogiclDiskName: Char; var FileList: TStringList; const bSort: Boolean = False): Boolean;  
  13.   
  14. implementation  
  15.   
  16. type  
  17.   PFileInfo = ^TFileInfo;  
  18.   
  19.   TFileInfo = record  
  20.     strFileName: String;               // 文件名称  
  21.     FileReferenceNumber: UInt64;       // 文件的ID  
  22.     ParentFileReferenceNumber: UInt64; // 文件的父ID  
  23.   end;  
  24.   
  25. const  
  26.   PARTITION_IFS          = $07;  
  27.   BUF_LEN                = 500 * 1024;  
  28.   USN_DELETE_FLAG_DELETE = $00000001;  
  29.   c_UInt64Root           = 1407374883553285;  
  30.   
  31. type  
  32.   PARTITION_INFORMATION = record  
  33.     StartingOffset: LARGE_INTEGER;  
  34.     PartitionLength: LARGE_INTEGER;  
  35.     HiddenSectors: Cardinal;  
  36.     PartitionNumber: Cardinal;  
  37.     PartitionType: Byte;  
  38.     BootIndicator: Boolean;  
  39.     RecognizedPartition: Boolean;  
  40.     RewritePartition: Boolean;  
  41.   end;  
  42.   
  43.   CREATE_USN_JOURNAL_DATA = record  
  44.     MaximumSize: UInt64;  
  45.     AllocationDelta: UInt64;  
  46.   end;  
  47.   
  48.   USN_JOURNAL_DATA = record  
  49.     UsnJournalID: UInt64;  
  50.     FirstUsn: Int64;  
  51.     NextUsn: Int64;  
  52.     LowestValidUsn: Int64;  
  53.     MaxUsn: Int64;  
  54.     MaximumSize: UInt64;  
  55.     AllocationDelta: UInt64;  
  56.   end;  
  57.   
  58.   MFT_ENUM_DATA = record  
  59.     StartFileReferenceNumber: UInt64;  
  60.     LowUsn: Int64;  
  61.     HighUsn: Int64;  
  62.   end;  
  63.   
  64.   PUSN = ^USN;  
  65.   
  66.   USN = record  
  67.     RecordLength: Cardinal;  
  68.     MajorVersion: Word;  
  69.     MinorVersion: Word;  
  70.     FileReferenceNumber: UInt64;  
  71.     ParentFileReferenceNumber: UInt64;  
  72.     USN: Int64;  
  73.     TimeStamp: LARGE_INTEGER;  
  74.     Reason: Cardinal;  
  75.     SourceInfo: Cardinal;  
  76.     SecurityId: Cardinal;  
  77.     FileAttributes: Cardinal;  
  78.     FileNameLength: Word;  
  79.     FileNameOffset: Word;  
  80.     FileName: PWideChar;  
  81.   end;  
  82.   
  83.   DELETE_USN_JOURNAL_DATA = record  
  84.     UsnJournalID: UInt64;  
  85.     DeleteFlags: Cardinal;  
  86.   end;  
  87.   
  88. { TStringList 按数值排序 }  
  89. function Int64Sort(List: TStringList; Index1, Index2: Integer): Integer;  
  90. var  
  91.   Int64A, Int64B: Int64;  
  92. begin  
  93.   Int64A := PFileInfo(List.Objects[Index1])^.FileReferenceNumber;  
  94.   Int64B := PFileInfo(List.Objects[Index2])^.FileReferenceNumber;  
  95.   if Int64A < Int64B then  
  96.     Result := -1  
  97.   else if Int64A = Int64B then  
  98.     Result := 0  
  99.   else  
  100.     Result := 1;  
  101. end;  
  102.   
  103. { 简化的 MOVE 函数,也可以用 MOVE 函数来替代 }  
  104. procedure MyMove(const Source; var Dest; Count: NativeInt); assembler;  
  105. asm  
  106.   FILD    QWORD PTR [EAX]  
  107.   FISTP   QWORD PTR [EDX]  
  108. end;  
  109.   
  110. { 获取文件全路径,包含路径和文件名 }  
  111. procedure GetFullFileName(var FileList: TStringList; const chrLogiclDiskName: Char; const bSort: Boolean = False);  
  112. var  
  113.   UInt64List: TArray<UInt64>;  
  114.   III       : Integer;  
  115.   UPID      : UInt64;  
  116.   intIndex  : Integer;  
  117. begin  
  118.   { 将 FileList 按 FileReferenceNumber 数值排序 }  
  119.   FileList.Sorted := False;  
  120.   FileList.CustomSort(Int64Sort);  
  121.   
  122.   { 将排序好的 FileReferenceNumber 复制到 UInt64 数组列表中,便于下面进行快速查找 <TArray.BinarySearch 为高效的折半查找> }  
  123.   SetLength(UInt64List, FileList.Count);  
  124.   for III := 0 to FileList.Count - 1 do  
  125.   begin  
  126.     UInt64List[III] := PFileInfo(FileList.Objects[III])^.FileReferenceNumber;  
  127.   end;  
  128.   
  129.   { 获取每一个文件全路径名称 }  
  130.   for III := 0 to FileList.Count - 1 do  
  131.   begin  
  132.     UPID := PFileInfo(FileList.Objects[III])^.ParentFileReferenceNumber;  
  133.     while TArray.BinarySearch(UInt64List, UPID, intIndex) do  
  134.     begin  
  135.       UPID                  := PFileInfo(FileList.Objects[intIndex])^.ParentFileReferenceNumber;  
  136.       FileList.Strings[III] := PFileInfo(FileList.Objects[intIndex])^.strFileName + '\' + FileList.Strings[III];  
  137.     end;  
  138.     FileList.Strings[III] := (chrLogiclDiskName + ':\' + FileList.Strings[III]);  
  139.   end;  
  140.   
  141.   { 将所有文件按文件名排序 }  
  142.   if bSort then  
  143.     FileList.Sort;  
  144. end;  
  145.   
  146. { 获取磁盘所有文件列表 }  
  147. function GetLogicalDiskAllFiles(const chrLogiclDiskName: Char; var FileList: TStringList; const bSort: Boolean = False): Boolean;  
  148. var  
  149.   Info       : PARTITION_INFORMATION;  
  150.   bStatus    : Boolean;  
  151.   hRootHandle: THandle;  
  152.   hTempHandle: Cardinal;  
  153.   cujd       : CREATE_USN_JOURNAL_DATA;  
  154.   ujd        : USN_JOURNAL_DATA;  
  155.   med        : MFT_ENUM_DATA;  
  156.   dujd       : DELETE_USN_JOURNAL_DATA;  
  157.   dwRet      : DWORD;  
  158.   Buffer     : array [0 .. BUF_LEN - 1] of Char;  
  159.   UsnRecord  : PUSN;  
  160.   strFileName: String;  
  161.   int64Size  : Integer;  
  162.   pfi        : PFileInfo;  
  163.   III        : Integer;  
  164.   
  165. begin  
  166.   Result := False;  
  167.   
  168.   { 打开磁盘 需要管理员权限 }  
  169.   hTempHandle := 0;  
  170.   hRootHandle := CreateFile(PChar('\\.\' + chrLogiclDiskName + ':'), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, hTempHandle);  
  171.   if hRootHandle = INVALID_HANDLE_VALUE then  
  172.     Exit;  
  173.   
  174.   try  
  175.     { 是否是NTFS磁盘格式 }  
  176.     if not DeviceIoControl(hRootHandle, IOCTL_DISK_GET_PARTITION_INFO, nil, 0, @Info, Sizeof(Info), dwRet, nil) then  
  177.       Exit;  
  178.   
  179.     if not Info.RecognizedPartition then  
  180.       Exit;  
  181.   
  182.     if Info.PartitionType <> PARTITION_IFS then  
  183.       Exit;  
  184.   
  185.     { 初始化USN日志文件 }  
  186.     bStatus := DeviceIoControl(hRootHandle, FSCTL_CREATE_USN_JOURNAL, @cujd, Sizeof(cujd), nil, 0, dwRet, nil);  
  187.     if not bStatus then  
  188.       Exit;  
  189.   
  190.     { 获取USN日志基本信息 }  
  191.     bStatus := DeviceIoControl(hRootHandle, FSCTL_QUERY_USN_JOURNAL, nil, 0, @ujd, Sizeof(ujd), dwRet, nil);  
  192.     if not bStatus then  
  193.       Exit;  
  194.   
  195.     { 枚举USN日志文件中的所有记录 }  
  196.     med.StartFileReferenceNumber := 0;  
  197.     med.LowUsn                   := 0;  
  198.     med.HighUsn                  := ujd.NextUsn;  
  199.     int64Size                    := Sizeof(Int64);  
  200.     while DeviceIoControl(hRootHandle, FSCTL_ENUM_USN_DATA, @med, Sizeof(med), @Buffer, BUF_LEN, dwRet, nil) do  
  201.     begin  
  202.       { 找到第一个 USN 记录 }  
  203.       UsnRecord := PUSN(Integer(@(Buffer)) + int64Size);  
  204.       while dwRet > 60 do  
  205.       begin  
  206.         { 获取文件名称 }  
  207.         strFileName := PWideChar(Integer(UsnRecord) + UsnRecord^.FileNameOffset);  
  208.         strFileName := Copy(strFileName, 1, UsnRecord^.FileNameLength div 2);  
  209.   
  210.         { 将文件信息添加到列表中 }  
  211.         pfi                            := AllocMem(Sizeof(TFileInfo)); // 不要忘记释放内存  
  212.         pfi^.strFileName               := strFileName;  
  213.         pfi^.FileReferenceNumber       := UsnRecord^.FileReferenceNumber;  
  214.         pfi^.ParentFileReferenceNumber := UsnRecord^.ParentFileReferenceNumber;  
  215.         FileList.AddObject(strFileName, TObject(pfi));  
  216.   
  217.         { 获取下一个 USN 记录 }  
  218.         if UsnRecord.RecordLength > 0 then  
  219.           Dec(dwRet, UsnRecord.RecordLength)  
  220.         else  
  221.           Break;  
  222.         UsnRecord := PUSN(Cardinal(UsnRecord) + UsnRecord.RecordLength);  
  223.       end;  
  224.       MyMove(Buffer, med, int64Size);  
  225.     end;  
  226.   
  227.     { 获取文件全路径,包含路径和文件名 }  
  228.     GetFullFileName(FileList, chrLogiclDiskName, bSort);  
  229.   
  230.     { 释放内存 }  
  231.     for III := 0 to FileList.Count - 1 do  
  232.     begin  
  233.       FreeMem(PFileInfo(FileList.Objects[III]));  
  234.     end;  
  235.   
  236.     { 删除USN日志文件信息 }  
  237.     dujd.UsnJournalID := ujd.UsnJournalID;  
  238.     dujd.DeleteFlags  := USN_DELETE_FLAG_DELETE;  
  239.     DeviceIoControl(hRootHandle, FSCTL_DELETE_USN_JOURNAL, @dujd, Sizeof(dujd), nil, 0, dwRet, nil);  
  240.   finally  
  241.     CloseHandle(hRootHandle);  
  242.   end;  
  243. end;  
  244.   
  245. end.  

Delphi 10.2 环境下开发。

调用:

var FileList : TStringList;

begin

  FileList     := TStringList.Create;

  GetLogicalDiskAllFiles('C', FileList, False);

  FileList.SaveToFile('d:\temp.txt', TEncoding.UTF8);

  FileList.Free;

end;

在我的机器上100万个文件,耗时7秒左右。200万个文件,耗时15秒左右。

posted @ 2018-06-25 08:29  h2z  阅读(1225)  评论(0)    收藏  举报