簡體   English   中英

如何動態加載和調用BPL包中的函數

[英]How to dynamically load and call a function in a BPL package

看起來很簡單,但是下面的代碼不起作用。

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')但是它也不起作用。 我在這里想念什么?

經過數小時的搜索,反復試驗和錯誤,我意識到它必須與我所做或做的事情有所不同。 問題是我的第一個版本mymod.bpl放入了Delphi的默認BPL輸出目錄(根本沒有導出,也沒有DoSomething())。 然后,我將BPL輸出目錄更改為項目的根源目錄,以便可以在一處看到源代碼和bpl模塊。 該exe文件沒有像以前在Delphi 7中那樣放到源代碼所在的位置,而是在Debug或Release文件夾下。 誤導我的是,當LoadPackage()在exe的當前目錄(即Debug / Release)中找不到模塊時,它將查看Delphi的默認包文件夾(該文件夾具有bpl的第一個和錯誤的版本)並加載它,所以沒有錯誤,但也沒有DoSomething(),因為它不再由模塊的編譯更新。

我希望這種解釋能幫助其他可能遇到類似問題的人弄清楚。 感謝所有抽出時間閱讀本文檔並發表評論的人。

參見下面的一段代碼。 它在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的項目。 僅包含一個Tform和一個TButton。 (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.

您必須在聲明中添加stdCall。

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

exports
  DoSomething;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM