• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

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

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

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

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

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


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap