-1

I have a bad behavior using GetProcAddress() to calling a simple method inside a Delphi package.

I have a singleton object that has some methods, and when I call any singleton method inside a Delphi package using GetProcAddress(), another instance of the singleton is being created. It is a big problem because there are a lot of methods that initialize services when the application is started.

Below is the simple example to share the problem:

Singleton Object

unit Unit2;

interface

uses System.Classes;

type
 TMyClass = class(TPersistent)
  strict private
    class var FInstance : TMyClass;
  private
    class procedure ReleaseInstance();
  public
    constructor Create;
    class function GetInstance(): TMyClass;
    procedure TheMethod; -->>> Any method
  end;

implementation

uses
  Vcl.Dialogs;

{ TMyClass }

constructor TMyClass.Create;
begin
  inherited Create;
end;

class function TMyClass.GetInstance: TMyClass;
begin
 if not Assigned(Self.FInstance) then
   Self.FInstance := TMyClass.Create;
  Result := Self.FInstance;
end;

class procedure TMyClass.ReleaseInstance;
begin
  if Assigned(Self.FInstance) then
    Self.FInstance.Free;
end;

procedure TMyClass.TheMethod;
begin
  ShowMessage('This is a method!');
end;

initialization

finalization
  TMyClass.ReleaseInstance();

end.

Package Source Code

unit Unit3;

interface

uses Unit2;

procedure CustomMethod;

implementation

procedure CustomMethod;
begin
 TMyClass.GetInstance.TheMethod; // ----->> callimg this method, another instance is initialized and lost the first settings
end;

exports
  CustomMethod;

begin

end.

Main program code

procedure TForm1.Button1Click(Sender: TObject);
var
  Hdl: HModule;
  P: procedure;
begin
  TMyClass.GetInstance.TheMethod; // -------->>> Initialize the singleton class normally
  Hdl := LoadPackage('CustomPgk.bpl');
  if Hdl <> 0 then
  begin
    @P := GetProcAddress(Hdl, 'CustomMethod'); //// ---->>> Call the custom method
    if Assigned(P) then
      P;

    UnloadPackage(Hdl);
  end;
end;

Can somebody help me, please?

Ken White
  • 123,280
  • 14
  • 225
  • 444
Dr. T
  • 19
  • 4
  • First of all, please format the question so the code is readable. Then, why are you using dynamic linking? Use load time linking with runtime packages and all will behave as you expect. – David Heffernan Aug 18 '22 at 04:08
  • How does it works please? "Load time linking" Thank you! – Dr. T Aug 18 '22 at 23:21
  • 1
    @Dr.T read Delphi's documentation, particularly [Loading Packages in an Application](https://docwiki.embarcadero.com/RADStudio/en/Loading_Packages_in_an_Application) and [Custom Packages](https://docwiki.embarcadero.com/RADStudio/en/Custom_Packages), and more generally [Working with Packages and Components](https://docwiki.embarcadero.com/RADStudio/en/Working_with_Packages_and_Components_Index) – Remy Lebeau Aug 19 '22 at 03:32

1 Answers1

1

The only way the Main program code can compile as shown is if it uses Unit2 to get the definition of TMyClass, which means the Main program must be compiled with its own copy of Unit2 since the package is not loaded yet.

In which case, LoadPackage() should fail, since you would have Unit2 being compiled into two separate modules that are loaded in the same process, which is a big no-no, and defeats the point of using packages.

But, even if LoadPackage() didn't fail, you would just end up with two separate FInstance variables in memory, one in the Main program's copy of Unit2, and one in the BPL's copy of Unit2. Which is why you end up with two objects created.

Both modules need to share a single copy of Unit2 in memory to avoid that. So, you need to either:

  • load the package statically at program load time, instead of dynamically.

  • move Unit2 to a 2nd package that both the Main program and the 1st package can load statically, and make sure "Runtime Packages" are enabled in all three modules.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770