Windows 在jvPlugin dll中创建表单并将其显示为邮件表单上控件的子级时出现问题

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}'

我正在尝试基于jvPlugin为我的应用程序编写一个插件系统。我在插件dll中创建表单,然后将它们重新租入DevExpress停靠控件。乍一看,这似乎奏效了。问题是dll窗体上的任何控件都不会接收焦点。此外,当单击诸如TSplitter之类的控件时,会引发“控件“xxx”没有父窗口”异常

以下是我的做法(浓缩版)

插件主机实现了一个IPluginHost接口

  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的不同副本是由内存管理器的不同实例造成的,而使用运行时软件包进行构建会导致它们共享同一内存管理器。