I have the following code:
Project.dpr
program Project2;
uses
madExcept,
madLinkDisAsm,
madListHardware,
madListProcesses,
madListModules,
Spring.Container,
Vcl.Forms,
uRegistrations in '..\Memory leak II\uRegistrations.pas',
Unit3 in 'Unit3.pas' {MainForm},
Unit4 in 'Unit4.pas' {SecondaryForm},
Unit5 in 'Unit5.pas';
{$R *.res}
begin
RegisterTypes(GlobalContainer);
Application.Initialize;
Application.MainFormOnTaskbar := True;
// MainForm:=TMainForm.Create(nil);
Application.CreateForm(TMainForm, MainForm);
MainForm.SecondaryForm := Globalcontainer.Resolve<ISecondaryForm>;
Application.Run;
end.
uRegistrations.pas that registers the interface
unit uRegistrations;
interface
uses
Spring.Container;
procedure RegisterTypes(Container: TContainer);
implementation
uses
Unit5,
Unit4;
procedure RegisterTypes(Container: TContainer);
begin
container.RegisterType<ISecondaryForm, TSecondaryForm>.DelegateTo(
function: TSecondaryForm
begin
result := TSecondaryForm.Create(nil);
end);
Container.Build;
end;
end.
Unit3.pas holding the main form
unit Unit3;
interface
uses
Winapi.Windows,
Winapi.Messages,
System.SysUtils,
System.Variants,
System.Classes,
Vcl.Graphics,
Unit5,
Vcl.Controls,
Vcl.Forms,
Vcl.Dialogs;
type
TMainForm = class(TForm)
private
{ Private declarations }
FSecondaryForm: ISecondaryForm;
public
{ Public declarations }
property SecondaryForm: ISecondaryForm read FSecondaryForm write FSecondaryForm;
end;
var
MainForm: TMainForm;
implementation
{$R *.dfm}
end.
Unit4.pas with the secondary form
unit Unit4;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
unit5,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
type
TSecondaryForm = class(TForm, ISecondaryForm)
private
{ Private declarations }
public
{ Public declarations }
end;
//var
// SecondaryForm: TSecondaryForm;
implementation
{$R *.dfm}
end.
and finally Unit5.pas with the Interface declaration
{$M+}
unit Unit5;
interface
type
ISecondaryForm=interface
['{62D63E9A-A3AD-435B-8938-9528E70D78B1}']
end;
implementation
end.
It compiles and runs regularly but when I close the application i have three memory leaks.
allocation number: 8482 program up time: 721 ms type: Brush Handle handle: $461027f5 style: BS_SOLID color: $f0f0f0
allocation number: 8318 program up time: 697 ms type: TSecondaryForm address: $d51ac64 size: 924 access rights: read/write
allocation number: 8267 program up time: 693 ms type: Font Handle handle: $1d0a28f1 face: Tahoma height: -11
Why does this happens and how can I solve it ?
EDIT
After the answer, I implemented the following solutions (the comments highlight the errors I got:
procedure RegisterTypes(Container: TContainer);
begin
container.RegisterType<ISecondaryForm, TSecondaryForm>.DelegateTo(
function: TSecondaryForm
begin
result := TSecondaryForm.Create(nil);
result.Owner:=Application.MainForm;//cannot assign to a read-only property
result.Parent:=Application; //incompatible types
result.Parent:=application.MainForm;//memory leak
end);
Container.Build;
end;
I have also tried to amend the OnClose method of TSecondaryForm in the following way:
procedure TSecondaryForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=caFree; //memory leak
end;
but I got a memory leak.
What am I doing wrong with all the technique above ?
In the end I just made the two methods _AddRef and _Release manage the reference counting as suggested in the comments and I have no more memory leaks.
TSecondaryForm = class(TForm, ISecondaryForm)
private
{ Private declarations }
protected
FRefCount: Integer;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
{ Public declarations }
end;
function TSecondaryForm._AddRef: Integer;
begin
Result := InterlockedIncrement(FRefCount);
end;
function TSecondaryForm._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result=0 then
self.Free;
end
If you want a form (or any class that inherits from TComponent) to be handled by interface reference counting then you need to implement it yourself (look at System.TInterfacedObject
as an example of how to do it).
You basically need to reimplement IInterface
to the class you want to enable reference counting on:
type
TInterfacedForm = class(TForm, IInterface)
// look at System.TInterfacedObject
end;
If you are doing so, keep in mind that it then should not be handled by the owner mechanism. If you register it to the container and use its default creation mechanism it will pass nil to the owner as of Spring4D 1.2 - see Spring.Container.Resolvers.TComponentOwnerResolver
). In any version before you need to explicitly create it with nil inside of DelegateTo
.
If you are dealing with any controls over interfaces that are put onto other controls (like frames) via their parent property keep also in mind that in such case another memory management mechanism comes into play which might destroy such a component if its parent is getting destroyed - if you are just dealing with interfaced forms that is not a problem but I thought I mention it here for completeness.