我正在使用Delphi XE6和VCL样式。我有主要的应用程序和dll。我的主应用程序已启用运行时主题,我正在使用vcl样式文件。我对我的DLL做了类似的事情。我启用了运行时主题,并在其中添加了VCL.Themes,VCL.Styles以及其中包含VCL样式文件的资源文件。当加载DLL时,我从资源加载VCL样式并将其设置为DLL gui。主应用程序和DLL不是使用运行时包构建的。
现在我的主应用程序GUI风格与自己的风格和DLL gui风格也有自己的风格。这似乎工作正常,直到......
当我点击主应用程序中的按钮时,该事件打开TPopupMenu,它的样式与DLL GUI相同,而不是主应用程序样式。如果我浏览菜单,我也会得到AV并且程序崩溃。看一下附图。
我做错了什么?我目前看到的唯一解决方法是使我自己定制的TPopupMenu来自其他控件。
正如我所承诺的,我准备了一个类似于我的应用程序的简单演示程序。它由具有自己样式的主机应用程序和添加到资源的样式的DLL组成。运行它并单击Popup按钮然后尝试从弹出窗口中选择一些内容。它会崩溃并停在一些StdWindowProc或类似的东西。此外,如果您在尝试从该菜单中选择某些内容时转到窗口系统菜单(左上角),您会注意到系统菜单的样式为DLL gui并且也崩溃了。链接到rar文件:dropbox.com/sh/f2jmbsmw18akpyg/AAA6SWdBmVhf6n6K-mvYLLmua?dl=0
感谢您的帮助。
答案 0 :(得分:9)
正如大卫所说,这是因为每个VCL实例安装一个钩子来检测何时创建一个弹出菜单(#32768)。所以有两个钩子实例同时工作。
作为解决方法,您可以使用Vcl.SysStyles单元中定义的UnRegisterSysStyleHook函数禁用dll(或应用程序中)中的popupmenu样式挂钩。
TCustomStyleEngine.UnRegisterSysStyleHook('#32768', TSysPopupStyleHook);
答案 1 :(得分:7)
这是VCL样式的基本问题以及它们样式菜单的方式。样式使用流程范围的钩子实现。特别是通过SetWindowsHookEx
单位TCustomStyleEngine.CreateSysHook
对Vcl.Themes
的调用安装的CBT挂钩。实际上,钩子只适用于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,主机可执行文件不会遇到您在问题中描述的问题。