Delphi - 创 build 计良好的插件

标签 delphi plugins modular

我发现工作良好的代码片段可以轻松创建模块化的 Delphi 应用程序。此代码在 Delphi 5 版本中运行良好。

链接到代码片段 -> http://delphi.cjcsoft.net/viewthread.php?tid=44129

我多年来一直在构建大型 Delphi 5 项目,并且我开始想知道是否可以加载用较新版本的 Delphi(在我的例子中为 XE2)编译的插件 (*.plg)。

我用 Borland 的 ShareMem 替换了 ShareMemRep.pas,因为 XE2 无法使用 ShareMemRep 进行编译。 Delphi 5 版本的插件和客户端应用程序正在加载用 Delphi 5 编写的插件,到目前为止运行良好。

我使用了相同的代码(除了我必须将 PAnsiChar 更改为 PWideChar)并使用 Delphi XE2 编译插件和客户端应用程序。但是编译后的客户端应用程序既无法加载使用Delphi XE2编译的插件,也无法加载使用Delphi 5编译的插件。

此外,Delphi 5 客户端应用程序无法加载使用 Delphi XE2 编译的插件。

Delphi XE2 客户端未加载用 Delphi XE2 插件编译的,因为 (PlugInClass.GetInterface(PlugInGUID, PlugInIntf​​)) 返回 False

当 Delphi XE2 或 Delphi 5 客户端应用程序加载相反版本的插件编译时,(PlugInClass.GetInterface(PlugInGUID, PlugInIntf​​)) 会导致访问冲突。

我从 System.pas 中发现,一些更改是在底层进行的,但我对这种“黑客位”的了解是纯粹的。

有人知道是否可以加载使用与使用代码片段中的代码加载该插件的应用程序不同的版本编译的插件(库)吗?

编辑: 我的源代码: https://bitbucket.org/plum/delphimodularapp

关闭插件加载的源代码(管理器):

function TPlugInManager.LoadPlugIn(const AFileName: string;
  PlugInGUID: TGUID; out PlugInIntf; ForceCreate: Boolean = False): Boolean;
var
  FileName: string;
  DLLHandle: THandle;
  FuncPtr: TFarProc;
  PlugInProc: TPlugInProc;
  PlugInClass: TPlugInClass;
  PlugInStruct: PPlugInStruct;
begin
  { initialize variables }
  Result := False;
  FileName := AFileName;
  DLLHandle := 0;
  try
    { try to load passed dll }
    // Delphi XE
    //DLLHandle := LoadLibraryW(PWideChar(AFileName));
    // Loading *.plg plugin
    DLLHandle := LoadLibrary(PAnsiChar(AFileName));
    if DLLHandle <> 0 then
    begin
      { get function address of 'RegisterPlugIn' }
      FuncPtr := GetProcAddress(DLLHandle, 'RegisterPlugIn');
      if FuncPtr <> nil then
      begin
        { assign register method }
        @PlugInProc := FuncPtr;

        { create plugin instance }
        PlugInClass := TPlugInClass(PlugInProc(FOwner, ForceCreate));  // creates instance!
        { the only tricky-part: accessing the common interface }
        if Assigned(PlugInClass) then begin
          // On that line I'm getting AV when I'm trying to load
          // plugin compiled with Delphi XE2 by Host application compiled with Delphi5.
          if (PlugInClass.GetInterface(PlugInGUID, PlugInIntf)) then
          begin
            { save plugin properties }
            New(PlugInStruct);
            PlugInStruct.AClass := PlugInClass;
            PlugInStruct.GUID := PlugInGUID;
            PlugInStruct.Handle := DLLHandle;
            PlugInStruct.AInterface := Pointer(PlugInIntf);
            PlugInStruct.FileName := AFileName;
            FPlugIns.Add(PlugInStruct);
            Result := True;
          end;
        end;

        if Result = False then begin
          FreeLibrary(DLLHandle);
        end;
      end;
    end;    // try/finally
  except
    on e: Exception do
    begin
      FLastError := e.Message;
      if DLLHandle <> 0 then
        FreeLibrary(DLLHandle);
    end;
  end;    // try/except
end;

插件代码(库):

library libSamplePlugIn;

uses
  ShareMem,
  Windows,
  Classes,
  uPlugInIntf in 'uPlugInIntf.pas',
  uSamplePlugInIntf in 'uSamplePlugInIntf.pas',
  uSamplePlugInImpl in 'uSamplePlugInImpl.pas';

{$E plg}  //
{$R *.res}

procedure DllMain(Reason: Integer);
begin
  case Reason of
   { our 'dll' will be unloaded immediantly, so free up the shared
     datamodule, if created before! }
    DLL_PROCESS_DETACH:
     { place your code here! }
  end;
end;

function RegisterPlugIn(AOwner: TComponent;
  ForceCreate: Boolean = False): TSamplePlugIn; stdcall;
begin
  Result := TSamplePlugIn.Create(AOwner);
end;

exports RegisterPlugIn;

begin
end.

和插件类:

unit uSamplePlugInImpl;

interface

uses
  uPlugInIntf, uSamplePlugInIntf, Classes;

type
{ TSamplePlugIn }

  TSamplePlugIn = class(TPlugInClass, ICustomPlugIn, ISamplePlugIn)
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function GetAuthor: string;
    function GetName: string;
    function GetVersion: string;
    function GetDescription: string;
    function Sum(a, b: Integer): Integer;
  end;

implementation

{ TSamplePlugIn }

constructor TSamplePlugIn.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
end;

{ Rest of implementation ...}

function TSamplePlugIn.Sum(a, b: Integer): Integer;
begin
  Result := a + b;
end;

end.

最佳答案

此代码由于致命的设计缺陷而被破坏。您不能跨 DLL 边界传递 Delphi 类并让它在另一边有意义。当您将 TComponent 传递给 DLL 并返回 TSamplePlugIn 时,该规则就被打破了。

您的主要选择:

  1. 切换到运行时包,或者
  2. 使用接口(interface)而不是类。

你看起来并不遥远。只需停止传递该 TComponent 即可。寻找另一种管理生命周期的方法。接口(interface)已经为此提供了引用计数。并返回一个接口(interface)而不是类实例。那么你应该上路了。

您确实刚刚遵循了那篇文章所设定的引导。遗憾的是,它似乎是由没有足够专业知识的人编写的。它可能在 D5 中可以工作,但在那里也被破坏了。你只是以某种方式逃脱了惩罚。

请注意,PChar 在 Unicode Delphi 中为 PWideChar,在 ANSI Delphi 中为 PAnsiChar。利用这一事实将允许您编写适用于这两种语言的代码。

DLLHandle := LoadLibrary(PChar(AFileName));

DllMain 是虚假的。删除它。

关于Delphi - 创 build 计良好的插件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28477986/

相关文章:

delphi - 如何在delphi上访问midi percussion?

python - 在delphi XE2中访问DLL,从Python转换

javascript - Indesign 插件的外部配置文件

grails - Grails 1.3.7的CXF插件版本

jQuery 插件不运行

java - 如何创建模块化 JSF 2.0 应用程序?

html - 在 colgroup 上设置行跨度?

html - 克隆按钮的 Angular 方式是什么?

Delphi烦恼, "find declaration"经常不起作用

delphi - 如何为相互依赖的记录定义隐式转换运算符?