delphi 如何在BPL包中动态加载和调用函数

abithluo  于 2023-10-18  发布在  其他
关注(0)|答案(4)|浏览(260)

这看起来很简单,但下面的代码不起作用。
BPL:

procedure DoSomething();
begin
  LogEvent('Did');
end;

exports
  DoSomething;

主EXE:

procedure CallModuleFunc;
var
  H: THandle;
  P: procedure();
begin
  H := LoadPackage('mymod.bpl');
  try
    if (H <> 0) then
    begin
      @P := GetProcAddress(H, 'DoSomething');
      if Assigned(P) then
        P();
    end;
  finally
    UnloadPackage(H);
  end;
end;

现在没有错误了,bpl成功加载了LoadPackage(),但是GetProcAddress()返回nil。为什么呢?可能是因为名字乱了。我尝试添加stdcall(导出函数和P的声明),但这并没有解决问题。我在网上看到了数百个例子,它应该是这样工作的。我甚至试过GetProcAddress(H, 'DoSomething$qqsv'),但它也不工作。我错过了什么?

yduiuuwa

yduiuuwa1#

经过几个小时的搜索和尝试和错误,我意识到这必须是关于我做的事情或做得不同。问题是我的mymod.bpl的第一个版本被放到了 Delphi 的默认BPL输出目录中(没有导出,根本没有DoSomething())。然后,我将BPL输出目录更改为项目的源代码根目录,这样我就可以在一个地方看到源代码和bpl模块。exe没有像以前在 Delphi 7中那样放在源代码所在的位置,而是放在“安装”或“发布”文件夹下。误导我的是,当LoadPackage()无法在exe的当前目录中找到模块时(该目录为“已发布/已发布”),它会查看 Delphi 的默认包文件夹(该文件夹中有bpl的第一个错误版本)并加载它,因此那里没有错误,但也没有DoSomething(),因为它不再被我的模块编译更新。
我希望这个解释能帮助其他可能有类似问题的人弄清楚事情。感谢所有花时间阅读本文并发表评论的人。

lnlaulya

lnlaulya2#

也许太晚了,但我想提出我的解决方案,这取决于ChatGPT提示:
首先,你必须打开一个新的BPL项目。然后你可以在BPLUnit.pas(class'es,Function's,Procedure's,...)中编写代码。对于这一点,最好的做法是,写一个“接口”。接口不支持构造函数、析构函数、虚函数、抽象函数和过程)。
这里是 Delphi 的代码,它展示了如何对界面单元进行编码:

unit Interfaces;
interface

type
  PointerByte = ^Byte;

type
  IRF_Package = interface(IInterface)
    ['{12345678-1234-1234-1234-CAFEBABE0000}']
    function DeCompressBuffer(str: String; len: Integer): Boolean;
    function DeCompressAll: Boolean;
  end;

implementation
end.

下面是接口实现代码(BPL项目代码):

unit ImplInterface;
interface
uses
  Windows, Classes, SysUtils, Dialogs, Interfaces;
type
  TRF_Package = class(TInterfacedObject, IRF_Package)
  public
    function DeCompressBuffer(str: String; len: Integer): Boolean;
    function DeCompressAll: Boolean;

    constructor Create; overload;
    destructor Destroy; override;
  end;

implementation

constructor TRF_Package.Create;
begin
  inherited Create;
  <your code logic>
end;

destructor TRF_Package.Destroy;
begin
  <your code logic>
  inherited Destroy;
end;

function TRF_Package.DeCompressBuffer(
  str: String;
  len: Integer): Boolean;
begin
  <your code logic>
  result := true;
end;
function TRF_Package.DeCompressAll: Boolean;
begin
  <your code logic>
  result := true;
end;

end.

现在,您可以使用接口(在EXE项目中):

procedure InterfaceProcedureInAPPcode;
var
  MyPackage : IRF_Package;
  MyGUID    : TGUID;

  procedure LoadBPL;
  var
    H: HModule;
  begin
    H := 0;
    try
      try
        H := LoadPackage('Package.bpl');
        if H = 0 then
        begin
          raise Exception.Create(
          #10 + 'Can not load package: Package.bpl' +
          #10 + 'aborted.');
        end;

        MyGUID := StringToGUID(
        '{12345678-1234-1234-1234-CAFEBABE0000}');
        MyPackage := CreateComObject( MyGUID ) as IRF_Package;

        if not Assigned(MyPackage) then
        begin
          raise Exception.Create(
          'can not access Interface: IRF_Package');
        end;

        if not(MyPackage.DeCompressAll) then
        begin
          raise Exception.Create(
          'bpl error.');
        end;
      except
        on E: Exception do
        begin
          ShowMessage('Exception:' +
          #10 + E.CLassName +
          #10 + E.Message  );
        end;
      end;
    finally
      UnloadPackage(H);
    end;
  end;
begin
  LoadBPL;
end;

有了这个代码,你不需要使用“导出”关键字。
希望我能帮上忙。

vatpfxk5

vatpfxk53#

下面是一段代码。它运行在 Delphi XE3下。

// Package declaration
package Package1;

{$R *.res}
{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
{$DEBUGINFO ON}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS ON}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION OFF}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{$REFERENCEINFO ON}
{$SAFEDIVIDE OFF}
{$STACKFRAMES ON}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST OFF}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$DEFINE DEBUG}
{$ENDIF IMPLICITBUILDING}
{$IMPLICITBUILD ON}

requires
  rtl,
  vcl;

contains
  Unit1 in 'Unit1.pas';

end.

单位Unit1.pas

unit Unit1;

interface
uses Vcl.Forms;

procedure Test(); stdcall;

exports
   Test;

implementation

procedure Test();
var F : TForm;
begin
  F := TForm.Create(nil);
  F.ShowModal;
  F.Release;
end;

end.

测试BPL的项目。只包含一个带有一个TButton的Tform。(Package1.bpl与project1.exe在同一目录中)

unit Unit2;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm2 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Déclarations privées }
  public
    { Déclarations publiques }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
type
   TProcTest = procedure;
var
   PackageModule: HModule;
   proc : TProcTest;
begin
  PackageModule := LoadPackage('Package1.bpl');
  if PackageModule <> 0 then
  begin
    @Proc := GetProcAddress( PackageModule, 'Test' );
    if @Proc <> nil then
      Proc;
    UnloadPackage(PackageModule);
  end;
end;

end.
cczfrluj

cczfrluj4#

您必须在声明中添加stdCall。

procedure DoSomething();stdcall;
begin
  LogEvent('Did');
end;

exports
  DoSomething;

相关问题