Delphi - 创建精心设计的插件

时间:2015-02-12 12:52:05

标签: delphi plugins modular

我找到了很好的代码片段,可以轻松创建模块化的Delphi应用程序。此代码与Delphi 5版本配合良好。

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

多年来我有很多Delphi 5的项目构建,我开始怀疑是否有可能加载使用较新版本的Delphi编译的插件(* .plg)(在我的情况下是XE2)。

我用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中发现,有些内容会在行底部发生变化但我的知识纯粹是关于那种' hack bits'。

有没有人知道是否有可能加载使用不同版本编译的插件(库),而不是使用来自代码段的代码加载该插件的应用程序?

编辑: 我的源代码: 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.

2 个答案:

答案 0 :(得分:4)

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

您的主要选择:

  1. 切换到运行时包,或
  2. 使用接口而不是类。
  3. 你似乎并不遥远。只是停止传递TComponent。找到另一种管理生命的方法。接口已经为此提供引用计数。并返回一个接口而不是一个类实例。然后你应该在路上。

    你确实只是遵循了那篇文章的主导。可悲的是,它似乎是由没有足够专业知识的人写的。它似乎可能在D5中起作用,但它也在那里打破了。你只是以某种方式逃脱了它。

    请注意,Unicode {1}}在Unicode Delphi中为PChar,在ANSI Delphi中为PWideChar。使用这个事实可以让你编写适用于两者的代码。

    PAnsiChar

    DLLHandle := LoadLibrary(PChar(AFileName)); 是虚假的。删除它。

答案 1 :(得分:0)

实际上,该信息不正确或至少不完整。如果你在应用程序和插件dll'(s)中都使用了基于com的内存管理器,你可以轻松地传递类实例。一个例子(旧但工作)在这里: http://thaddy.co.uk/commm.pas

顺便说一下:我试图在当时正在进行的内存管理器竞赛中写出最慢的替代品。没有用,它不够慢;)