科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件在VisualStudio中使用Windows桌面搜索

在VisualStudio中使用Windows桌面搜索

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

计算机硬盘容量越来越大,您保存的信息量也越来越多。您拥有成千上万的文件和电子邮件消息,因此很难准确查找所需的信息。令人欣慰的是,Windows? 桌面搜索可以帮助您进行查找。

作者:Sergey Mishkovskiy 来源:微软 2007年10月16日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
后台查询和 UI 更新

  早期的设计目标之一是构建一个几乎不影响 Visual Studio 响应性的工具。Windows 桌面搜索运行索引查询时,查询的执行速度会极快。然而根据搜索内容,执行一次查询仍会花费几毫秒到一秒,或更长的时间。若查询是在 Visual Studio 主线程上执行的,那么运行查询时,UI 会出现暂时的锁定。明显的解决方案就是在次级线程上执行查询。

  您可能会考虑使用新的 .NET Framework 2.0 BackgroundWorker 类。不幸的是,这行不通。因为该类与 SearchDesktopClass 及其 Windows Desktop Search COM 对象不兼容。尝试使用 BackgroundWorker 执行操作会产生 InvalidCastException 异常。要在次级线程上使用 SearchDesktopClass,您就必须使用单线程单元 (STA) 线程。BackgroundWorker 可以使用 .NET ThreadPool,默认情况下,其中会包含多线程单元 (MTA) 线程。

  因此,添加一个名为 WDSQueryWorker 的新类到加载项项目,可以将 Run 方法用作线程启动委托参数来创建一个 STA 线程。Run 方法使用 WaitHandle 类等待单独的事件句柄,如下所示:

以下是引用片段:
  private void Run() {
  try {
  while (true) {
  WaitHandle.WaitAny(_runHandles);
  if (_stopped) break;
  ProcessEvent();
  _runEvent.Reset();
  }
  }
  finally { _stopped = true; }
  }

  WDSQueryWorker 类还提供一个名为 DoWork 的公共方法,用来设置 Run 方法的等待事件,随后还会在 ProcessEvent 方法中触发查询执行。要查询的字段应在调用 DoWork 之前设置。

  WDSQueryWorker 类构造函数采用了三个参数:查询完成时调用的 UI 控件、查询完成委托和查询错误委托。两个委托都通过 Control.Invoke 调用。Invoke 方法可确保委托在控件的 UI 线程上执行。值得一提的是,传递到查询完成委托的参数都包含 _Resultset。另外,查询错误委托参数还包含 Exception 引用。

  虽然将查询执行移至单独线程,可以解决某些与运行大型查询相关的 Visual Studio 响应性问题,但仍存在一个有待解决的问题。像提到的那样,控件的 Invoke 方法在控件的 UI 线程上执行委托,并且该线程恰巧是 Visual Studio UI 线程。如果 Windows 桌面搜索返回一个大型结果集,则仍需花费时间填充加载项的结果列表视图。明显的解决方案是限制显示结果。事实上,如果您已启用“根据输入进行搜索”选项,Windows 桌面搜索将只显示每个类别的前六个结果,同时会添加一个“more…”项以显示其余结果。使用“选项”对话框,您可以设置加载项来限制显示结果,比方说,默认每个类别显示 100 项。如果您想看到整个结果集,可将“显示首批结果”的值设置为“0”以取消限制。

  您还希望确保当编辑器中存在用户类型代码时,即使具有受限的查询结果,也不会因查询执行而导致延迟。幸运的是,Visual Studio 可扩展性恰好为此提供了编辑器事件。可通过 Events 接口访问这些事件。图 6 显示了如何进行设置。

  基本思路是,如果用户正在输入代码、滚动代码或从一个窗口移至另一窗口,则延迟查询。您可以添加自己的委托,并将所有这些事件的全部委托设置为调用私有 EditorUpdateInProgress 方法,以延迟查询执行。

  将其放在一起

  当我着手创建该加载项时,目标是当用户选择 Visual Studio 编辑器中的某文本时仍然可以执行查询,而且我希望最初能找到一些 Visual Studio 可扩展性文本选定事件。虽然 TextSelection 可扩展性接口可返回当前选定的文本,但可扩展性并不提供文本选定更改事件。这是一个令人遗憾的退步。唯一的解决方案是轮询,但会再度产生 Visual Studio 响应性问题。轮询本质上是一种代价高昂的操作,因为您会在不必要的时候结束轮询。因此需要建立仔细的安全措施,以防止过度轮询。

  一种方法是为您的用户控件添加计时器,并设置为每两秒滴答一次。也可以为“选项”对话框页面添加几个较慢的计时器刷新设置。计时器的 Tick 事件处理程序仅调用私有 ExecuteQuery 方法,设置查询和初始化次级工作线程处理就在此进行,如图 7 所示。为强调该方法的目的,一个很妙的主意是,将其命名为与 Windows Desktop Search SDK 的方法名称 (SearchDesktopClass.ExecuteQuery) 相匹配的名称。

  当 ExecuteQuery 运行时,首先进行的操作就是将私有处理字段设置为“true”,并禁用轮询计时器。然后检索 Visual Studio 活动文档,并检索其带有 Text 属性的 TextSelection(如果有)。该属性会返回编辑器中当前选定的文本。

  将最后一次执行的选定保存在私有字段中。检索 TextSelection.Text 后,请检查当前选定文本是否与上次文本查询有所不同。如果相同,就不必重新执行,只需退出该方法即可。

  您还应按照 选项对话框页面中的配置,检查选定文本是否满足最低选定文本要求。选定的文本至少应包含三个执行查询的字符,即针对用户选择的性能保障,例如,如果只输入一个字母 A,Windows 桌面搜索将返回数千个条目。

  如果一切正常,将使用要搜索的文本设置查询工作线程(以前定义的 WDSQueryWorker 类),并且会调用它的 DoWork 方法。ExecuteQuery 方法结束时,计时器将重新启动(如果事先是运行的)并且处理字段将重新设置为“false”。结果列表视图将被清除,更新状态也将被更改,从而显示“Searching…”。

  查询工作线程的 DoWork 方法将设置一个事件,用于执行一次查询。当然,所有这些工作都可以在次级线程上执行。当查询完成时,该方法将回调到主线程。回调委托恰好支持 ToolWindowUserControl。其中的 QueryCompleted 方法将只调用 DisplayResults 来更新结果列表视图。如果查询失败,它还会调用 ToolWindowUserControl 的 QueryError 方法。根据“选项”对话框页面中的配置,QueryError 可显示错误消息或无提示地忽略错误。

  DisplayResults 方法将收到一个 _Recordset 参数。如果重新调用,Windows Desktop Search SDK ExecuteQuery 方法就会返回该参数。DisplayResults 将遍历结果,每次提取一行。它可以根据预定义的一组查询字段检索列值,并更新相应的列表视图列。它还可以根据 PerceivedType 列对项目进行分组,Windows 桌面搜索可依此识别项目类型。列表视图分组是 .NET Framework 2.0 的新功能,无法在 Windows XP 之前的系统上运行。

  除针对最小查询字符串长度具有保障外,您还可以限制显示的结果数,例如让每个类别只显示前 100 项。您可以将该选项更改为 0 以查看整个结果集。通过列表视图结果的合理的端,UI 更新处理几乎可以瞬间发生,这极大改善了 Visual Studio 的响应性。研究一下结果选项,看看哪种最适合您。

  存在潜在改进可能性的一个方面是,检查 Windows 桌面搜索是否支持在查询级别上限制结果集。即,如果将“显示首批结果”选项实现从调整显示结果转为调整实际查询范围,就可以加速该进程。

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章