Windows 在jvPlugin dll中创建表单并将其显示为邮件表单上控件的子级时出现问题
我正在尝试基于jvPlugin为我的应用程序编写一个插件系统。我在插件dll中创建表单,然后将它们重新租入DevExpress停靠控件。乍一看,这似乎奏效了。问题是dll窗体上的任何控件都不会接收焦点。此外,当单击诸如TSplitter之类的控件时,会引发“控件“xxx”没有父窗口”异常 以下是我的做法(浓缩版) 插件主机实现了一个IPluginHost接口Windows 在jvPlugin dll中创建表单并将其显示为邮件表单上控件的子级时出现问题,windows,delphi,plugins,focus,jedi,Windows,Delphi,Plugins,Focus,Jedi,我正在尝试基于jvPlugin为我的应用程序编写一个插件系统。我在插件dll中创建表单,然后将它们重新租入DevExpress停靠控件。乍一看,这似乎奏效了。问题是dll窗体上的任何控件都不会接收焦点。此外,当单击诸如TSplitter之类的控件时,会引发“控件“xxx”没有父窗口”异常 以下是我的做法(浓缩版) 插件主机实现了一个IPluginHost接口 IPluginHost = interface ['{C0416F76-6824-45E7-8819-414AB8F39E19}'
IPluginHost = interface
['{C0416F76-6824-45E7-8819-414AB8F39E19}']
function AddDockingForm(AForm: TForm): TObject;
function GetParentApplicationHandle: THandle;
end;
IMyPlugin = interface
['{E5574F27-3130-4EB8-A8F4-F709422BB549}']
procedure AddUIComponents;
end;
该插件实现了一个IMyPlugin接口
IPluginHost = interface
['{C0416F76-6824-45E7-8819-414AB8F39E19}']
function AddDockingForm(AForm: TForm): TObject;
function GetParentApplicationHandle: THandle;
end;
IMyPlugin = interface
['{E5574F27-3130-4EB8-A8F4-F709422BB549}']
procedure AddUIComponents;
end;
初始化插件时调用以下事件:
procedure TMyPlugin.JvPlugInInitialize(Sender: TObject; var AllowLoad: Boolean);
var
RealApplicationHandle: THandle;
begin
if Supports(HostApplication.MainForm, IPluginHost, FPluginHost) then
begin
RealApplicationHandle := Application.Handle;
Application.Handle := FPluginHost.GetParentApplicationHandle; // Returns Application.Handle from the host application
try
FMyPluginForm:= TMyPluginForm.Create(Application); // Plugin host app owns the form
finally
Application.Handle := RealApplicationHandle;
end;
end;
end;
加载插件主机后,我在插件中调用IMyPlugin.AddUIComponents。它是这样实现的:
procedure TMyPlugin.AddUIComponents;
begin
// Add the docking form
FPluginHost.AddDockingForm(FMyPluginForm);
end;
function TfrmMyPluginHost.AddDockingForm(AForm: TForm): TObject;
var
DockPanel: TdxDockPanel;
begin
// Create a new dockpanel
DockPanel := TdxDockPanel.Create(Self);
DockPanel.Name := DPName;
DockPanel.Height := AForm.Height;
DockPanel.DockTo(dxDockSite1, dtBottom, 0);
DockPanel.AutoHide := TRUE;
// Rename the dock panel and parent the plugin
DockPanel.Caption := AForm.Caption;
DockPanel.Tag := Integer(AForm);
AForm.Parent := DockPanel;
AForm.BorderStyle := bsNone;
AForm.Align := alClient;
AForm.Show;
FDockedPluginFormList.Add(AForm);
Result := DockPanel;
end;
AddDockingForm在主机中的实现方式如下:
procedure TMyPlugin.AddUIComponents;
begin
// Add the docking form
FPluginHost.AddDockingForm(FMyPluginForm);
end;
function TfrmMyPluginHost.AddDockingForm(AForm: TForm): TObject;
var
DockPanel: TdxDockPanel;
begin
// Create a new dockpanel
DockPanel := TdxDockPanel.Create(Self);
DockPanel.Name := DPName;
DockPanel.Height := AForm.Height;
DockPanel.DockTo(dxDockSite1, dtBottom, 0);
DockPanel.AutoHide := TRUE;
// Rename the dock panel and parent the plugin
DockPanel.Caption := AForm.Caption;
DockPanel.Tag := Integer(AForm);
AForm.Parent := DockPanel;
AForm.BorderStyle := bsNone;
AForm.Align := alClient;
AForm.Show;
FDockedPluginFormList.Add(AForm);
Result := DockPanel;
end;
如果我在插件窗体上的任何控件上运行以下函数,我会看到一个列表一直返回到主机的主窗体。但Tspliters告诉我他们没有父窗口。这怎么可能
function TfrmMyPlugin.GetParents(WC: TWinControl): String;
begin
if (WC <> nil) and (WC is TWinControl) then
Result := WC.Name + ' [' + WC.ClassName + '] - ' + GetParents(WC.Parent);
end;
函数TfrmMyPlugin.GetParents(WC:TWinControl):字符串;
开始
如果(WC nil)和(WC为TWinControl),则
结果:=WC.Name+'['+WC.ClassName+']-'+GetParents(WC.Parent);
结束;
我一定是在什么地方遗漏了什么。有人有什么好主意吗?用运行时包构建插件和宿主应用程序 在不使用运行时包的情况下,DLL使用RTL、VCL和任何使用过的单元的不同副本。例如,DLL的
TForm
类与主机的TForm
类不同(is
运算符在主机/DLL边界上失败,因此DLL控件不会将主机父窗体识别为TForm的有效实例)、全局变量,如应用程序
、鼠标
,Screen
是单独的实例,您有两份RTTI等
VCL的设计根本不是为了这样使用
打开运行时软件包后,所有问题都得到解决,插件(运行时软件包本身或使用运行时软件包构建的DLL)可以使用运行时软件包与主机应用程序无缝集成。我们使用“运行时软件包”添加“vcl、rtl、ourownpckg”
其中,ourownpckg是一个由我们自己创建的包,包含所有DX依赖项,以及我们跨exe插件(包括JVCL插件)使用的其他一些依赖项
这三个包必须随exe一起装运
希望它有帮助我不希望使用软件包进行构建。它添加了很多依赖项,在构建安装程序时,这些依赖项有时会被忽略。现在,我们只有几个exe文件。有没有一个原因可以解释为什么使用包进行构建会带来不同?不修改VCL源代码就可以吗。您的问题(以及您尚未注意到的其他问题)源自这样一个事实:在不使用运行时包的情况下,DLL使用RTL和VCL的不同副本。DLL的TForm与主机的TForm不同,全局变量(如应用程序、鼠标、屏幕等)是单独的实例。到目前为止,最简单的方法是使用运行时包。@TOndrej:+1。你应该把大部分的评论添加到你的实际答案中,因为这会使它更有意义。您可能还想指出,外接程序和应用程序之间的RTL和VCL的不同副本是由内存管理器的不同实例造成的,而使用运行时软件包进行构建会导致它们共享同一内存管理器。