在Delphi中使用VCL实现Shell命名空间扩展

3

Shell命名空间扩展非常复杂。在过去的十年中,我们一直在构建一个shell命名空间扩展;最新版本是MagicRAR的Archive Folders功能(www.magicrar.com)。

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

我们使用了各种工具,如AQTime Pro来排除故障,但没有报告任何内存覆盖或其他类似的访问问题。这只留下一个罪犯:VCL不是线程安全的!

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

主应用程序对象甚至不存在于我们的shell命名空间扩展DLL中。使用TThread.Synchronize会死锁Explorer,因为没有任何主VCL线程被创建。我们需要手动创建一个主VCL线程(如何?) - 可能在另一个DLL中 - 并通过该DLL重新路由我们所有的UI创建/更新/销毁吗?

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

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

我们还尝试过使用多个版本的ShellPlus组件。他们做了一些出色的工作,但不幸的是,在我们的经验中,即使基于他们的代码的非常基本的努力也比我们自己的代码提供了明显更差的结果。

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

因此,首先问题是理论性的:VCL是否可以用于使用Explorer中的VCL窗口作为进程主机的shell命名空间扩展中?

如果可以,如何处理此场景中的VCL线程安全问题?


1
你是否确实检查过涉及GUI的代码是否在一个进程中运行多个线程?Win32有自己的线程规则,具体来说,窗口具有线程亲和性。我期望每当资源管理器想让你处理一个窗口句柄时,你总是会在同一个线程中被调用。换句话说,我怀疑线程亲和性是否真正是你的问题所在。 - David Heffernan
如果资源管理器非常注意线程亲和性,那就很好了。但是,当多个资源管理器窗口显示我们的VCL窗口的多个副本时,会怎样呢?VCL不是线程安全的,因此仍然存在多个线程在同一进程内执行GUI工作的问题,而不是单个线程处理所有GUI工作。实际上,资源管理器创建了多个调用shell命名空间扩展的线程。 - user2118012
好的,那时你就完了。 - David Heffernan
这是否意味着Delphi在构建Shell命名空间扩展方面是不可行的,至少就GUI而言是这样吗? - user2118012
你必须疯了才会将FMX放入资源管理器进程中!这是一个巨大的错误工厂。与VCL不同,它也非常远离Win32。因此,我怀疑它是否适合。而且我不明白它为什么会有所帮助。 - David Heffernan
显示剩余2条评论
2个回答

4
VCL确实不支持多线程,即使在VCL应用程序之外运行也是如此。缺少主要的VCL线程只会让情况更糟;VCL控件期望在主线程的上下文中运行,而通常情况下不存在这样的线程,也无法存在。你使用的控件访问各种全局变量,但没有同步保护,除非修改所有VCL控件使用的单元,否则你无能为力。
在资源管理器中创建和操作窗口时,请使用常规的Windows API技术,而不是VCL函数。
通过确保某些内容在接收到指令后检查队列,你可以解决同步挂起问题,但是如果没有“主”线程,那么也不清楚你将与哪个线程同步,因此Synchronize可能并不是合适的工具。

我在想是否有意义尝试创建一个“主”VCL线程,它将拥有所有GUI工作,处理所有同步,并成为所有Synchronize()请求的目标。 - user2118012
请注意,如果无法使用VCL来管理shell命名空间扩展的GUI部分,则使用Delphi项目就失去了意义。如果这是不可能做到的,那么重新从头开始在另一种可能是线程安全的语言中重建项目可能更有意义。 - user2118012
@user 通常谈论“线程安全语言”是没有意义的。仅仅说“线程安全”有些含糊不清。你必须明确指定你所讨论的具体形式的线程安全性。你会发现,与其他选择相比,用Delphi编写扩展更容易些,比如C++。 - David Heffernan
如果语言附带的类库GUI部分不是线程安全的,那么对于需要这种线程的GUI项目来说,这将是一个无法解决的问题。那么FireMonkey呢?它是线程安全的吗? - user2118012
@user 你没有理解我的观点。“线程安全”是一个不精确的术语。例如,Win32在某些定义下是线程安全的。但如果超出了特定定义的限制,那么就会遇到线程错误。那么,一个库怎么能够既是线程安全的,又能够产生线程错误呢?你必须非常具体地使用这个术语。可以查看Eric Lippert的文章,“你所说的线程安全是什么?” - David Heffernan
我宁愿不参与这里的学究式讨论,我觉得“线程安全”的术语内涵在我的原始帖子和后续澄清中非常清晰。然而,如果FireMonkey是一个适合的、线程安全的VCL替代品,那么值得评估一下。 - user2118012

0

我们终于成功解决了这个问题。 修复后的 shell 命名空间扩展将会在即将发布的 MagicRAR 9.0 中被展现。 问题确实出在 VCL 上——除此之外,我们的代码完全没有问题。 对于任何试图使用 Delphi 构建 shell 命名空间扩展的人:

是的,你可以使用 Delphi 构建 shell 命名空间扩展。 但是,如果你使用 VCL,你会遇到无数随机崩溃和不稳定性。 如果你不使用 VCL,使用 Delphi 的意义也不大。 好消息是你不需要打补丁 VCL 源代码。只需使用专用的 VCL 线程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接