【问题标题】:File search in Delphi is ignoring some Windows directoriesDelphi 中的文件搜索忽略了一些 Windows 目录
【发布时间】:2017-01-06 03:15:51
【问题描述】:

我正在使用此线程 (How to Search a File through all the SubDirectories in Delphi) 中的代码递归查找文件:

procedure FindFilePattern(root:String;pattern:String);
var
  SR:TSearchRec;
begin
  root:=IncludeTrailingPathDelimiter(root);
  if FindFirst(root+'*.*',faAnyFile,SR) = 0 then
  begin
      repeat
          Application.ProcessMessages;
          if ((SR.Attr and faDirectory) = SR.Attr ) and (pos('.',SR.Name)=0) then
             FindFilePattern(root+SR.Name,pattern)
          else
          begin
           if pos(pattern,SR.Name)>0 then Form1.ListBox1.Items.Add(Root+SR.Name);
          end;
      until FindNext(SR)<>0;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FindFilePattern('C:\','.exe');
end;

它正在工作,但由于某种原因它忽略了目录 Program Files、Program Files (x86) 和 Users。不幸的是,我正在搜索的文件在这些文件夹中。有人知道为什么会这样吗?

任何提示都会非常有帮助,

谢谢

【问题讨论】:

  • 你说的似乎不太合理。您的代码不会跳过程序文件文件夹。

标签: windows delphi winapi


【解决方案1】:

您的应用程序需要以提升的权限运行,才能访问您所说的子目录中的某些子目录。作为临时测试,运行“以管理员身份”来验证这一点。如果这确实是您的情况,那么您应该添加一个清单以在启动时要求提升。

Delphi 的较新版本已内置此功能。在项目选项下,转到“应用程序”页面,然后选中“启用管理员权限”。当然,这是使用 IDE 的快捷方式,您应该考虑编写自己的自定义清单,尤其是在运行没有此选项的旧版 Delphi 时。

【讨论】:

  • 这适用于调用用户无法访问的目录,例如系统目录和属于其他用户的目录。一般来说,并非所有目录都如此。很有可能,枚举将能够对大部分目录进行只读访问。
  • @Remy OP 说“程序文件、程序文件 (x86) 和用户”是他们试图搜索的对象。
  • 除了你实际上不需要运行提升来枚举Program FilesUsers的大部分部分。我刚试了一下,没有提升效果很好。
  • @Remy 关键字“最” - 我们不确切知道 OP 期望哪些部分起作用。但是这些目录引发了很大的危险。
  • 程序文件(任一版本)没有读取限制。 write 限制可能需要提升权限,但任何人都可以从这些文件夹中读取。您也可以随时读取和写入您自己的用户文件夹以及公共文档树。
【解决方案2】:

就像 Jerry Dodge 提到的那样,担保权肯定是您可能需要考虑的一件事。您的代码可能需要以管理员身份运行才能访问各种系统目录和属于其他用户的目录。在大多数情况下,简单地枚举您提到的目录不需要提升权限。但是如果你发现你遇到了这样的目录,你可以:

  1. 将 UAC 清单添加到请求提升的应用中

  2. 将您的枚举代码移动到单独的进程或 COM 对象中,然后您可以在需要时从未提升的进程提升运行。

但除此之外,您复制的FindFilePattern() 程序一开始也执行错误。它是:

  1. 比较文件属性不正确。如果条目是一个文件,或者是一个带有 NO ATTRIBUTES 的目录,则表达式 (SR.Attr and faDirectory) = SR.Attr ) 将为真。遇到带有系统、压缩、索引等属性的目录并不少见。

  2. 比较文件名不正确。对于根本不包含. 字符的所有文件和目录,表达式(pos('.',SR.Name)=0) 将为真。几乎所有文件都有.,甚至目录也可以有.。处理目录时,您需要忽略特殊的... 目录条目,而不是任何包含. 的条目。

  3. 更糟糕的是,#1 和 #2 的组合共同作用允许将具有属性的目录和包含 . 的目录视为文件而不是目录。代码通常无法正确处理目录,更不用说递归了。

  4. 泄露所有搜索句柄。如果FindFirst() 成功,则必须调用FindClose(),而不管FindNext()

试试类似的方法:

uses
  SysUtils, Masks;

procedure FindFilePattern(root: String; pattern: String);
var
  SR: TSearchRec;
begin
  root := IncludeTrailingPathDelimiter(root);
  if FindFirst(root + '*.*', faAnyFile, SR) = 0 then
  try
    repeat
      Application.ProcessMessages;
      if (SR.Attr and faDirectory) <> 0 then
      begin
        if (SR.Name <> '.') and (Sr.Name <> '..') then
          FindFilePattern(root + SR.Name, pattern);
      end else
      begin
        if MatchesMask(SR.Name, pattern) then
          Form1.ListBox1.Items.Add(Root + SR.Name);
      end;
    until FindNext(SR) <> 0;
  finally
    FindClose(SR);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FindFilePattern('C:\', '*.exe');
end;

【讨论】:

  • 谢谢!工作就像一个魅力:)
  • 补充一点:使用Application.ProcessMessages 是一种避免阻塞用户界面的廉价技巧,但它应该带有警告标签。它产生了调用者不会意识到的重入代码的隐藏风险。在这种特殊情况下,效果可能只是ListBox1 输出中的一个良性特性;并通过添加重入防护轻松解决。但是,通常最好在单独的线程中进行繁重的处理。这更加明确,并且具有额外的好处,即该代码被“引导”到更好地分离关注点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-13
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
  • 1970-01-01
  • 2013-05-16
相关资源
最近更新 更多