0

我正在尝试为我的应用程序编写一个基于 jvPlugin 的插件系统。我在插件 dll 中创建表单,然后将它们重新设置为 DevExpress 停靠控件。乍一看,它似乎有效。问题是 dll 表单上的任何控件都没有获得焦点。此外,当单击 TSplitter 等控件时,会引发“控件‘xxx’没有父窗口”异常。

这是我的做法(精简版)。

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

  IPluginHost = interface
  ['{C0416F76-6824-45E7-8819-414AB8F39E19}']
    function AddDockingForm(AForm: TForm): TObject;
    function GetParentApplicationHandle: THandle;
  end;

该插件实现了一个 IMyPlugin 接口

  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;

AddDockingForm 在主机中实现如下:

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;

如果我在插件表单上的任何控件上运行以下函数,我会看到一个列表一直返回到主机的主表单。然而 TSplitters 告诉我他们没有父窗口。怎么会这样?

function TfrmMyPlugin.GetParents(WC: TWinControl): String;
begin
  if (WC <> nil) and (WC is TWinControl) then
    Result := WC.Name + ' [' + WC.ClassName + '] - ' + GetParents(WC.Parent);
end;

我一定在某处遗漏了什么。有人有什么好主意吗?

4

2 回答 2

1

使用运行时包构建插件和主机应用程序。

在不使用运行时包的情况下,DLL 使用 RTL、VCL 和任何使用的单元的不同副本。例如,DLL 的TForm类与宿主的TForm类不同(is运算符在宿主/DLL 边界上失败,因此 DLL 控件不会将宿主父窗体识别为 TForm 的有效实例),全局变量如Application, Mouse,Screen是单独的实例,您有两份 RTTI 等,等等。

VCL 根本不是为这样使用而设计的。

使用运行时包,所有问题都得到解决,插件(运行时包本身或使用运行时包构建的 DLL)可以使用运行时包与主机应用程序无缝集成。

于 2011-10-21T14:53:51.467 回答
0

我们使用“运行时包”添加“vcl,rtl,ourownpckg”使其工作,其中 ourownpckg 是使用所有 DX 依赖项和我们在 exe 插件(包括 JVCL 插件)中使用的其他一些依赖项创建的我们自己的包。

三个包必须随exe一起发

希望能帮助到你

于 2011-10-21T16:12:36.957 回答