Jump to content
softtouch

Returning a string from a bpl

Recommended Posts

How  must be the prototype of a function inside a bpl in order to return a string to the caller?

 

This is the test bpl code:

unit test;

interface

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

type
  TForm1 = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  function GetPluginName:string;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function GetPluginName:string;
begin
  result:='test';
end;


exports
  GetPluginName;

end.

 

And here is the test caller code:

 

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;


var
  Form1: TForm1;
  GetPluginName:function:string;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
	h:nativeuint;
	s:string;
begin
	h:=LoadPackage('test.bpl');
	@GetPluginName:=GetProcAddress(h, 'GetPluginName');
    s:=GetPluginName;
	UnloadPackage(h);
end;

end.

 

Edited by softtouch

Share this post


Link to post

This will work fine so long as everything is linked correctly. No need even to use stdcall. So probably the linkage is messed up. We can't see the entire picture. 

Share this post


Link to post
42 minutes ago, David Heffernan said:

This will work fine so long as everything is linked correctly. No need even to use stdcall. So probably the linkage is messed up. We can't see the entire picture. 

I updated the first post with the complete test code for bpl and caller. I removed the stdcall. The result is the same, an AV as long the bpl return anything. If I remove the line with the result:=..., no AV and its fine.

Share this post


Link to post

Can you try assigning the string constant to a variable first and/or calling UniqueString on result after assignment? The string literal may vanish when unloading the package.

Share this post


Link to post
5 minutes ago, Uwe Raabe said:

Can you try assigning the string constant to a variable first and/or calling UniqueString on result after assignment? The string literal may vanish when unloading the package.

I tried, but its the same.

 

var s:='Test';

result:=s;
UniqueString(result);

When I get the string from the caller code with 

s:=GetPluginName;

s contains 'Test', as it should. But when I unload the package, s will be "inaccessible" and I get an AV.

Share this post


Link to post

Perhaps a UniqueString(s) before unloading the package might work then?

 

It looks like the problem comes from the package invalidating the result string memory when unloading. The tricky part now is to find a workaround for this.

Share this post


Link to post
3 minutes ago, Uwe Raabe said:

Perhaps a UniqueString(s) before unloading the package might work then?

 

It looks like the problem comes from the package invalidating the result string memory when unloading. The tricky part now is to find a workaround for this.

A workaround would be this I guess:

var tmp:=GetPluginName;
s:=copy(tmp,1);
tmp:='';

"tmp" gets the correct text, so I copy that text to "s", and set "tmp" to nil, and "s" still contains the right text after unloading the bpl. If I dont set "tmp" to nil, it will crash. So its somehow related to the reference count I think.

Share this post


Link to post
On 8/18/2023 at 2:34 AM, softtouch said:

A workaround would be this I guess:

UniqueString() would have done the same thing with less code, eg:

s := GetPluginName;
UniqueString(s);

To make things easier on the caller, that should be done inside of the package function instead, eg:

function GetPluginName: string;
begin
  Result := 'test';
  UniqueString(result);
end;
On 8/18/2023 at 2:34 AM, softtouch said:

If I dont set "tmp" to nil, it will crash.

That makes sense if you are letting the compiler clear "tmp" AFTER the package has been unloaded, since "tmp" will be pointing at unloaded memory when the RTL tries to access it for cleanup,  Calling UniqueString()/Copy() BEFORE the package is unloaded addresses that problem.  Of course, you could also solve the problem by simply not returning a string literal from the function in the first place, or by ensuring the caller is completely finished using any data from the package before unloading it from memory.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×