这是 VCL 样式及其菜单样式方式的一个基本问题。样式是通过进程范围的钩子来实现的。具体来说,是通过调用安装的 CBT 挂钩SetWindowsHookEx
from TCustomStyleEngine.CreateSysHook
in the Vcl.Themes
单元。事实上,该钩子仅适用于 GUI 线程,但这是进程范围内的,因为进程中只有一个 GUI 线程。
由于您的应用程序中有多个 VCL 实例(一个在 DLL 中,一个在应用程序中),因此安装了两个挂钩。那太多了。最近安装的挂钩(恰好是 DLL)获胜,这就是 DLL 菜单样式感染您的可执行文件的原因。以及为什么会遇到访问冲突。 DLL 正在尝试对属于可执行文件的菜单进行操作。因此,尽管您尽了最大努力,但最终还是得到了从主机可执行文件访问 VCL 对象的 DLL 代码。
没有简单的方法可以解决这个问题并在两个模块中完全支持样式。我们这里得到的是设计的基本结果。该系统并未设计为支持多个 VCL 实例。如果您希望在多个模块中使用 VCL 样式,那么设计者希望您使用运行时包。
我想您也许能够通过在完全不同的线程中操作 DLL 来获得一些吸引力。这将涉及从该不同的线程加载 DLL,以便 VCL 在该线程中初始化。对 DLL 的所有调用都必须来自该线程。并且您需要在该线程中运行消息循环。你可能能够做到这一点,但我对此表示怀疑。即使提到了所有的附带条件,您仍然必须处理这样一个事实:您有两个 GUI 线程,这会带来输入队列处理的各种问题。
也许另一种方法是从 DLL 中卸载挂钩。只要您的 DLL 不显示菜单,那么您就可以卸载该挂钩。它会禁用 DLL 显示的菜单样式,但这也许是可以接受的。
这个版本的 DLL(在我对其进行了一些简化之后)会卸载该钩子。
library VCLStyleDLL;
{$R 'Style.res' 'Style.rc'}
uses
VCL.Styles,
VCL.Themes,
VCL.SysStyles; // to gain access to TSysPopupStyleHook
{$R *.res}
begin
TStyleManager.TrySetStyle('Glossy', false);
TCustomStyleEngine.UnRegisterSysStyleHook('#32768', TSysPopupStyleHook);
end.
使用此版本的 DLL,主机可执行文件不会遇到您在问题中描述的问题。