GetProcAddres and Singleton Object (Bad Behavior) - delphi

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?

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.

Related

Delphi reference counting for subclasses

Say that I have a situation like this:
ITest = interface
procedure somethingHere();
end;
TImpl = class(TInterfacedObject, ITest)
procedure somethingHere();
end;
TImplSub = class(TImpl)
end;
Given the code above I am able to use this kind of code without any memory leak if I don't use the try-finally statement:
var a: ITest;
begin
a := TImpl.Create;
end;
Is this the same for the subclass?
var a: ITest;
begin
a := TImplSub.Create;
end;
I think that since TImplSub is a subclass of TImpl, TImplSub inherits TInterfacedObject and ITest from the father. Does the above code leak?
This may be not related but how can I check if the code above leaks or not?
Reference counting for interface references is triggered with _AddRef and _Release methods that are in this case implemented in TInterfacedObject. Your subclass inherits that reference counting behavior.
You can use, actually you must use, interface references to store your subclassed object instance, the way you coded it. (Not using interface reference for storing reference counted object instances breaks reference counting mechanism)
Following code does not leak, and does not require try...finally block because destruction is automatic.
var a: ITest;
begin
a := TImplSub.Create;
end;
To check for memory leaks under Windows compiler you can use ReportMemoryLeaksOnShutdown
begin
ReportMemoryLeaksOnShutdown := true;
...
end.
Another way of testing whether object is destroyed while you are investigating specific behavior is to override destructor and set breakpoint there.
Thanks to the comments (#nil user) I have managed to make a test like this
type
ITest = interface
procedure test;
end;
TProva = class(TInterfacedObject, ITest)
procedure test;
end;
TProvaSub = class(TProva)
procedure testDue;
end;
And then if you try to run this code (in debug mode with F9):
procedure TForm1.Button1Click(Sender: TObject);
var a: ITest;
begin
a := TProvaSub.Create;
a.test;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ReportMemoryLeaksOnShutdown:=DebugHook<>0;
end;
WHen I close the form I DON'T have a leak report.
My conclusion: TProvaSub has a piece of TProva inside itself (since it's a subclass) and so it inherits the _AddRef and _Release. So the code is good and doesn't leak!

Access violation freeing library that returns a interface for a class within the dll

I have a dll that contains a class that implements a interface. The dll has an exported method that returns the interface.
I can explicit load the dll succefully, but when I try to use Free Library I get Access Violation. I did not tried use implicit link, because I need use the explicit mode.
If I just load the library and free right after, without geting the interface, everything works fine.
Dll
library Tef;
uses
uTTefFacade;
{$R *.res}
exports
CreateTef;
begin
end.
Interface in dll:
type
ITefFacade = interface
['{77691DD1-C6E9-4F75-951F-BFA1468DC36C}']
function IniciarTransacao(AParam: TTefIniciarTransacaoParamDTO): TTefIniciarTransacaoResultDTO;
end;
Class in dll:
type
TTefFacade = class (TInterfacedObject, ITefFacade)
private
function IniciarTransacao(AParam: TTefIniciarTransacaoParamDTO): TTefIniciarTransacaoResultDTO;
public
constructor Create;
destructor Free;
end;
function CreateTef: ITefFacade; export; stdcall;
function CreateTef: ITefFacade;
begin
Result := ITefFacade(TTefFacade.Create);
end;
Exe:
procedure TForm1.FormCreate(Sender: TObject);
var
CreateTef: function: ITefFacade; stdcall;
begin
try
FTef := nil;
FHTef := LoadLibrary('Tef.dll');
if (FHTef > 0) then
begin
#CreateTef := GetProcAddress(FHTef, 'CreateTef');
if (#CreateTef <> nil) then
FTef := CreateTef;
end;
if (FTef = nil) then
ShowMessage('Error.');
except
on E: Exception do
ShowMessage('Erro: ' + E.Message);
end;
end;
And here in the calling Free Library, access violation occurs.
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeLibrary(FHTef);
end;
You have to nil the FTef reference before releasing the DLL.
The object behind the interface lives in the DLL, you should respect this. If you try to unload the DLL without releasing the interface first, there will be problems when the object is accessed after the unload (such as when Delphi auto-nils the reference when it goes out of scope).

Delphi - Using TStringList in Class Definition (very new)

I'm doing a simple class definition in Delphi and I wanted to use a TStringList in the class & it's constructor (so everytime you create an object, you pass it a StringList and it does some magic stuff to the StringList data, copying the string list to it's own internal string list). The problem I get is that when I try to declare what it "uses" before the class definition (so it knows how to handle the TStringList), it fails on compile. But without that, it doesn't know what a TStringList is. So it seems to be a scoping issue.
Below is a (very simplified) class definition, similar to what I'm trying to do. Can someone suggest how I can make this work and get the scoping right?
I tried adding the uses statements at the project level as well, but it still fails. I wonder what I need to do to get this right.
unit Unit_ListManager;
interface
type
TListManager = Class
private
lmList : TStringList;
procedure SetList;
published
constructor Create(AList : TStringList);
end;
implementation
uses
SysUtils,
StrUtils,
Vcl.Dialogs;
constructor TBOMManager.Create(AList : TStringList);
begin
lmList := TStringList.Create;
lmList := AListList;
end;
procedure SetPartsList(AList : TStringList);
begin
lmList := AListList;
ShowMessage('Woo hoo, got here...');
end;
end.
Kind Regards
You didn't show where exactly you were adding the unit reference, but I'm betting it was the wrong place. Take note of the additional code between interface and type.
I've also corrected your definition of the constructor, which you had placed in published instead of public. Only property items belong in the published section.
unit Unit_ListManager;
interface
uses
Classes,
SysUtils,
StrUtils,
Vcl.Dialogs;
type
TListManager = Class
private
lmList : TStringList;
procedure SetList;
public
constructor Create(AList : TStringList);
end;
implementation
constructor TListManager.Create(AList : TStringList);
begin
inherited Create; // This way, if the parent class changes, we're covered!
// lmList := TStringList.Create; This would produce a memory leak!
lmList := AListList;
end;
procedure TListManager.SetList;
begin
// You never provided an implementation for this method
end;
end.

How can I make sure RTTI is available for a class without instantiating it?

I've recently posted a question in this forum asking for any advice regarding missing RTTI information in a DXE2 executable.
That post was a stripped down version of my actual case. RRUZ came to the rescue, and so the stripped down version was quickly resolved. The original problem, though, is still standing, and so I'm posting it in full now. "Main":
program MissingRTTI;
{$APPTYPE CONSOLE}
uses
System.SysUtils, RTTI, MyUnit in 'MyUnit.pas', RTTIUtil in 'RTTIUtil.pas';
var
RHelp: TRttiHelper;
begin
RHelp := TRttiHelper.Create();
if (RHelp.IsTypeFound('MyUnit.TMyClass')) then WriteLn('TMyClass was found.')
else WriteLn('TMyClass was not found.');
ReadLn;
RHelp.Free();
end.
RTTIUtil.pas:
unit RTTIUtil;
interface
uses
MyUnit;
type
TRttiHelper = class(TObject)
public
function IsTypeFound(TypeName: string) : boolean;
end;
implementation
uses
RTTI;
function TRttiHelper.IsTypeFound(TypeName: string): boolean;
var
rCtx: TRttiContext;
rType: TRttiType;
begin
Result := false;
rCtx := TRttiContext.Create();
rType := rCtx.FindType(TypeName);
if (rType <> nil) then
Result := true;
rCtx.Free();
end;
end.
and finally MyUnit.pas:
unit MyUnit;
interface
type
TMyClass = class(TObject)
end;
implementation
end.
The desired type is not found. However, if I change TRttiHelper.IsTypeFound so that it instantiates (and immediately frees) an instance of TMyClass, the type is found. Like so:
function TRttiHelper.IsTypeFound(TypeName: string): boolean;
var
rCtx: TRttiContext;
rType: TRttiType;
MyObj: TMyClass;
begin
Result := false;
MyObj:= TMyClass.Create();
MyObj.Free();
rCtx := TRttiContext.Create();
...
So I'm wondering, is there any way I can force RTTI to be emitted for TMyClass without actually instantiating it?
Update:
On a side not, I might mention that if I try to fetch the TRttiType using TRttiContext.GetType, the desired type is found. So there is some RTTI emitted. Checking the TRttiType.IsPublic property as retrieved by TRttiContext.GetType yields a true value, i.e. the retrieved type is public (and hence should be possible to locate using TRttiContext.FindType).
Add a reference to the class and make sure that the compiler/linker cannot strip it from the executable.
unit MyUnit;
interface
type
TMyClass = class(TObject)
end;
implementation
procedure ForceReferenceToClass(C: TClass);
begin
end;
initialization
ForceReferenceToClass(TMyClass);
end.
In production code you would want to place ForceReferenceToClass in a base unit so that it could be shared. The initialization section of the unit that declares the class is the most natural place for the calls to ForceReferenceToClass since the unit is then self-contained.
Regarding your observation that GetType can locate the type, the very act of calling GetType(TMyClass) adds a reference to the type to the program. It's not that the RTTI is present and FindType cannot find it. Rather, the inclusion of GetType(TMyClass) adds the RTTI to the resulting program.
I used {$STRONGLINKTYPES ON} and worked very well. Put it on main unit.

delphi tlb_lib file from com component has no connection between interface and object

After importing into delphi the com dll file, the delphi in turn generated a lib_tlb.pas file.
Inspecting the file it shows
Iinterface1 = interface(IDispatch)
function func: Integer; safecall;
procedure proc(param:Iinterface1);
end;
Cointerface1 = class
class function Create: Iinterface;
class function CreateRemote(const MachineName: string): Iinterface1;
end;
Tinterface1 = class(TOleServer)
function func: Integer;
procedure proc(param:Iinterface1);
end;
Now its clear to see that there is no connection between Tinterface1 and Iinterface1.
The problem comes when one calls proc with an Tinterface1. this will not compile Tinterface1 does not inheritce Iinterface1.
So what suggested to do? change the lib that is auto generated? or do you have a better idea of what to do when wanting to pass Tinterface1 to proc.
The example is a simplification of the code, in the code there is anther object that needs to be the one to be passed to proc, however that proc knows only its interface, which is the same problem.
update: as it seems the manual of the com dll file, says that proc should be
procedure proc(param:^Tinterface1);
where the interface is only in delphi point of view.
TInterface1.Proc() is expecting a pre-existing IInterface1 object to be passed to it as input. Use Cointerface1.Create() to create that object, eg:
var
intf: Iinterface1;
begin
intf := Cointerface1.Create;
TheOleServerInstance.proc(intf);
end;
Tinterface1 is a TOleServer descendant that does not directly inherit from Iinterface1 (but it does wrap an Iinterface1 internally), so you have to cast it whenever you want to pass it where an Iinterface1 is expected, eg:
var
intf: Iinterface1;
svr: Iinterface1;
begin
intf := Cointerface1.Create;
if Supports(TheOleServerInstance, Iinterface1, svr) then
intf.proc(svr);
end;

Resources