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

时间:2016-09-10 21:59:47

标签: delphi

看起来非常简单,但下面的代码不起作用。

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'),但它也没有用。我在这里缺少什么?

3 个答案:

答案 0 :(得分:1)

经过数小时的搜索,试验和错误后,我意识到这必须是关于我做或做的不同的事情。问题是我的第一个版本的mymod.bpl被放入Delphi的默认BPL输出目录(它没有导出,根本没有DoSomething())。然后,我将BPL输出目录更改为项目的根源目录,这样我就可以在一个地方看到源代码和bpl模块。 exe文件没有像以前那样存放在Delphi 7中,它位于Debug或Release文件夹下。误导我的是当LoadPackage()无法在exe的当前目录(即Debug / Release)中找到模块时,它会查看Delphi的默认包文件夹(它有bpl的第一个和第一个版本)并加载它,所以没有错误,但也没有DoSomething()因为它不再由我的模块的编译更新。

我希望这个解释可以帮助其他可能有类似问题的人解决问题。感谢所有不遗余力阅读本书和书面评论的人。

答案 1 :(得分:-1)

见下面的一段代码。 它在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.

答案 2 :(得分:-2)

您必须在声明中添加stdCall。

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

exports
  DoSomething;