【问题标题】:Shell Namespace Extension in Delphi Using the VCLDelphi 中使用 VCL 的 Shell 命名空间扩展
【发布时间】:2013-02-14 03:31:19
【问题描述】:

Shell 命名空间扩展相当复杂。在过去的 10 年里,我们一直在构建一个 shell 命名空间扩展;最新的版本是 MagicRAR (www.magicrar.com) 中的存档文件夹功能。

不幸的是,尽管编码非常仔细,确保线程正确访问共享内存等,我们的 shell 命名空间扩展仍然偶尔会崩溃。 Explorer 主机进程在使用我们的 shell 命名空间扩展期间或之外崩溃。

我们使用了多种工具(例如 AQTime Pro)来对我们的 shell 命名空间扩展代码进行故障排除。没有报告内存覆盖或其他类似的访问问题。这只剩下一个罪魁祸首:VCL 不是线程安全的!

确实,我们正在使用 VCL 作为 shell 命名空间扩展的一部分; Explorer 中的文件列表实际上是一个托管控件,在我们自己的 shell 命名空间扩展的情况下,它实际上是一个 VCL 窗口。我们现在甚至想知道(经过多代开发)这是否是一个允许的场景......

主应用程序对象甚至不存在于我们的外壳命名空间扩展 DLL 中。使用 TThread.Synchronize 死锁资源管理器,因为尚未在任何地方创建主 VCL 线程。我们是否需要手动创建一个主 VCL 线程(如何?) - 可能在另一个 DLL 中 - 并通过该 DLL 重新路由我们所有的 UI 创建/更新/销毁?

请记住,资源管理器可能会显示任意数量的包含我们的 VCL 窗口的窗口。根据目标系统的配置,Explorer 还可以作为多个独立进程或单个进程运行。

我们的 shell 命名空间扩展基于 John Lam 的起点(大多数 Delphi shell 命名空间开发人员都知道)。当然,正如您在最终产品中看到的那样,对这个起点进行了重大修改。 John Lam 甚至从未在他的幻灯片和示例项目中讨论过 VCL 线程不安全的问题。

在过去十年中,我们还尝试使用多个版本的 ShellPlus 组件。他们已经完成了一些出色的工作,但不幸的是,根据我们的经验,即使是基于他们的代码的非常初级的工作也提供了比我们自己的代码差得多的结果。

ShellPlus 实际上还提供了使用 Explorer 自己的预定义主机窗口的功能,而不是创建自定义 VCL 窗口;虽然这可能会回避任何 VCL 线程问题,但根据我们的经验,即使这也不是一个可行的解决方案 - 因为 ShellPlus shell 命名空间扩展始终不如我们自制的代码稳定,无论 VCL 是否窗口化。

所以首先;这个问题是一个理论上的问题 - VCL 可以在使用 Explorer 内的 VCL 窗口作为进程主机的 shell 命名空间扩展中使用吗?

如果是这样,在这种情况下如何处理VCL线程不安全的问题?

【问题讨论】:

  • 您是否真正检查过您的涉及 GUI 的代码是否在每个进程中运行多个线程? Win32 有自己的线程规则。具体来说,窗口具有线程亲和性。我希望只要资源管理器希望您使用窗口句柄,您总是会在同一个线程中被调用。换句话说,我质疑线程亲和力是否真的是你的问题。
  • 如果资源管理器注意线程亲和性,那就太好了,但是当多个资源管理器窗口显示多个我们的 VCL 窗口副本时会发生什么? VCL 不是线程安全的,因此仍然存在多个线程在同一进程中执行 GUI 工作的问题——而不是单个线程处理所有 GUI 工作。事实上,Explorer 创建了多个调用 shell 命名空间扩展的线程。
  • 好的,到那时你就完蛋了。
  • 那么这是否意味着 Delphi 不适合构建 shell 命名空间扩展,至少就 GUI 而言?
  • 一点也不。如果你可以在 C++ 中完成,你可以在 Delphi 中完成。但 VCL 听起来不是一个选项。

标签: delphi com vcl


【解决方案1】:

我们终于能够解决这个问题。 固定的 shell 命名空间扩展将在即将推出的 MagicRAR 9.0 中提供。 问题确实出在 VCL 上——否则我们的代码非常好。 对于其他尝试使用 Delphi 构建 shell 命名空间扩展的人:

是的,您可以使用 Delphi 构建 shell 命名空间扩展。 但是 - 如果您使用 VCL,您将遇到无数随机崩溃和不稳定。 并且 - 如果您不使用 VCL,那么使用 Delphi 毫无意义。 好消息是您不需要修补 VCL 源。只需使用专用的 VCL 线程即可。

【讨论】:

    【解决方案2】:

    VCL 确实不是线程安全的。在 VCL 应用程序之外运行并不能缓解该规则。缺少一个主 VCL 线程只会让事情变得更糟; VCL 控件期望在主线程的上下文中运行,一般来说没有这样的线程,也不可能有。您使用的控件在没有任何同步保护的情况下访问各种全局变量,除了修补任何 VCL 控件使用的所有单元之外,您无能为力。

    使用常规的 Windows API 技术,而不是 VCL 函数,在资源管理器中创建和操作窗口。


    您可以通过确保在被告知时检查队列来解决同步挂起的问题,但如果没有“主”线程,则不清楚您将同步什么,所以Synchronize只是可能不是适合这项工作的工具。

    【讨论】:

    • 我想知道尝试创建一个“主”VCL 线程是否有意义,该线程将拥有所有 GUI 工作,处理所有同步,并成为所有 Synchronize() 请求的目标。
    • 请注意,如果 VCL 不能用于管理 shell 命名空间扩展的 GUI 部分,那么它就违背了在项目中使用 Delphi 的目的。如果这根本不可能做到,那么用另一种可能是线程安全的语言从头开始重建项目可能更有意义。
    • @user 谈论“线程安全语言”通常是没有意义的。单独的“线程安全”有点毫无意义。您必须准确指定您谈论的特定形式的线程安全。您仍然会发现在 Delphi 中编写扩展比在替代 C++ 中更容易。
    • 如果随语言一起提供的类库的 GUI 部分不是线程安全的,那么对于需要这种线程的 GUI 项目来说,这是一个阻碍。 FireMonkey 怎么样,那个线程安全吗?
    • @user 你没明白我的意思。线程安全是一个不精确的术语。例如,根据线程安全的某些定义,Win32 是线程安全的。但是,如果您超出该特定定义的约束,那么您会遇到线程错误。那么,一个库如何是线程安全的,但又能产生线程错误呢?您必须非常具体地使用该术语。查找 Eric Lippert 的文章,你称之为线程安全的东西是什么?
    猜你喜欢
    • 2015-05-25
    • 2012-08-28
    • 2012-09-02
    • 2013-06-10
    • 1970-01-01
    • 1970-01-01
    • 2011-12-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多