I run in this situation where Destroy() is never called.
unit Unit2;
interface
type
// Interface
ITest = Interface(IInterface)
function IsTrue() : Boolean;
end;
TmyClass = class(TInterfacedObject, ITest)
public
// Interface implementation
function IsTrue() : Boolean;
constructor Create();
destructor Destroy(); override;
end;
implementation
constructor TmyClass.Create();
begin
inherited Create();
end;
destructor TmyClass.Destroy();
begin
inherited Destroy();
end;
published
// Property
property IsItTrue: Boolean read IsTrue;
end.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms,
Vcl.Dialogs, Vcl.StdCtrls, unit2;
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
fMyClass: TmyClass;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
fMyClass.Free; // if refcount = 0 this works, if refcount <> 0 pointer error.
//or
fMyClass := Nil; // no error but Destroy wil not be executed
Close();
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
fMyClass := TMyClass.Create();
end;
end.
Reading this article, there is only a constructor but no destructor implemented.
Is there any particular reason for this?
And should I release (if needed) all other objects that will be defined in myClass by implementing a finalization section?
The most likely reason for the destructor not being called would be because you don't assign your object to an interface variable.
procedure Test1;
var
vMyObj : TObject;
begin
vMyObj := myclass.Create;
end; <-Destructor NOT called here
procedure Test2;
var
vMyIntf : IInterface;
begin
vMyIntf := myclass.Create;
end; <-Destructor IS called here.
If that's the case, I invite you to read this answer for more information.
Your fMyClass variable is an object reference, not an interface, so it does not participate in TInterfaceObject's reference counting.
You need to change this:
fMyClass: TmyClass;
to this:
fMyClass: ITest;
And then you can get rid of fMyClass.Free; altogether:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms,
Vcl.Dialogs, Vcl.StdCtrls, unit2;
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
fMyClass: ITest;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
fMyClass := nil; // no error and Destroy will be executed
Close();
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
fMyClass := TMyClass.Create();
end;
end.
fMyClass := nil; will invoke reference counting only if fMyClass is an interface variable, not an object reference, and you can't call Free() on an interface variable.
Related
Need to access some components right after program start but I have found that doing so from form's onCreate event is bad because at the moment they may still be unavailable (access violation occurs). Can not find onCreate event in any component. Am I missing something?
Here is code. Empty form with ValueListEditor.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.ValEdit;
type
TForm1 = class(TForm)
ValueListEditor1: TValueListEditor;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure: Load;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
Load;
end;
procedure Load;
begin
(Application.MainForm.FindComponent('ValueListEditor1') as TValueListEditor)
.Strings.LoadFromFile('c:\li');
end;
end.
The problem is not because your component hasn't been created yet, because it has been. The real problem is because the Application.MainForm property hasn't been assigned yet when your main Form's OnCreate event is fired, so you are calling FindComponent() on a nil Form pointer.
Since Load() is merely accessing a member of TForm1 then Load() should also be a member of TForm1 as well, and then you can call it, and thus access your component, via the implicit Self pointer, which is valid during the Form's OnCreate event, eg:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.ValEdit;
type
TForm1 = class(TForm)
ValueListEditor1: TValueListEditor;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure LoadValues;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadValues;
end;
procedure TForm1.LoadValues;
begin
ValueListEditor1.Strings.LoadFromFile('c:\li');
end;
end.
If, for whatever reason, Load() must be a standalone procedure, then at least have it use your global Form1 variable, which the call to Application.CreateForm() will assign before the main Form's OnCreate event is fired, eg:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.ValEdit;
type
TForm1 = class(TForm)
ValueListEditor1: TValueListEditor;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure LoadValues;
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure Load;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadValues;
end;
procedure TForm1.LoadValues;
begin
ValueListEditor1.Strings.LoadFromFile('c:\li');
end;
procedure Load;
begin
if Form1 <> nil then
Form1.LoadValues;
end;
end.
Alternatively, you can fallback to looking for the Form1 object in the Screen.Forms[] array, eg:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.ValEdit;
type
TForm1 = class(TForm)
ValueListEditor1: TValueListEditor;
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
procedure LoadValues;
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure Load;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadValues;
end;
procedure TForm1.LoadValues;
begin
ValueListEditor1.Strings.LoadFromFile('c:\li');
end;
procedure Load;
var
I: Integer;
Frm: TForm;
begin
for I := 0 to Screen.FormCount-1 do
begin
Frm := Screen.Forms[I];
if Frm is TForm1 then
begin
TForm1(Frm).LoadValues;
Exit;
end;
end;
end;
end.
Use the form's onShow() -event.
But be aware, that the onShow() -event is called every time the form is shown, not only the first time.
In your form, you should override the DoShow method and insert your code there. Since DoShow is called everytime the form changes from invisible to visible, you should also add a boolean variable in your form and check in the DoShow override, if it is false. In that case, it is the first time DoShow is called. Then set the variable to true and do whatever you need to do the first time.
Please note that within DoShow the form is not visible yet. If you need to do something once the form is made visible, you can post a custom message from DoShow and put your code in the corresponding message handler. At the time it is executed, the form just became visible.
const
WM_APP_STARTUP = WM_USER + 1;
type
TForm1 = class(TForm)
protected
FInitialized : Boolean;
procedure DoShow; override;
procedure WMAppStartup(var Msg: TMessage); message WM_APP_STARTUP;
end;
implementation
procedure TForm1.DoShow;
begin
// Form is NOT visible but will soon be
if not FInitialized then begin
FInitialized := TRUE;
// Insert your code here
PostMessage(Handle, WM_APP_STARTUP, 0, 0);
end;
inherited DoShow;
end;
procedure TForm1.WMAppStartup(var Msg: TMessage);
begin
// The form is now visible
// Insert your code here
end;
Create a simple VCL application:
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 FormDestroy(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
JclStringLists;
var
MyList1: TJclStringList;
MyList2: TJclStringList;
procedure TForm1.FormDestroy(Sender: TObject);
begin
MyList1.Free;
MyList2.Free;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
MyList1 := TJclStringList.Create;
MyList2 := TJclStringList.Create;
MyList1.LoadFromFile('C:\ONE.txt');
MyList2.LoadFromFile('C:\TWO.txt');
Self.Caption := Self.Caption + ' ' + IntToStr(MyList1.Count);
Self.Caption := Self.Caption + ' ' + IntToStr(MyList2.Count);
end;
end.
It crashes in the TForm1.FormDestroy event-handler when attempting to free the MyList1 object instance. Why?
TJclStringList is a reference counted type (it's declared in JCLStringLists.pas as type TJclStringList = class(TJclInterfacedStringList, IInterface, IJclStringList) and implements both _AddRef and _Release to handle reference counting), so you shouldn't be creating them as objects at all, and you shouldn't manually free them - they will automatically be free'd when the reference to them goes out of scope. (This also means you should not declare them as global variables, because you then don't maintain control over their lifetime.)
The JclStringLists unit provides several functions that will properly create an instance of the interface for you. You can see them in that unit, just above the implementation keyword:
function JclStringList: IJclStringList; overload;
function JclStringListStrings(AStrings: TStrings): IJclStringList; overload;
function JclStringListStrings(const A: array of string): IJclStringList; overload;
function JclStringList(const A: array of const): IJclStringList; overload;
function JclStringList(const AText: string): IJclStringList; overload;
The proper way to use TJclStringList to do what you want is something like this:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, JCLStringLists;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private declarations }
MyList1, MyList2: IJCLStringList; // Note I and not T in type.
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
MyList1 := JclStringList;
MyList1.LoadFromFile('C:\Work\Data\FirstName.txt');
MyList2 := JclStringList
MyList2.LoadFromFile('C:\Work\Data\LastName.txt');
// Only to demonstrate that both files got loaded by the code above.
Self.Caption := Format('First: %d Last: %d', [MyList1.Count, MyList2.Count]);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
// Do NOT free the JclStringLists here - they will automatically be released when
// the form is destroyed because the reference count will reach zero (as long as
// you don't have any other references to those variables, which by putting them into
// the private section is unlikely.
end;
end.
I am trying to create a Form and a Frame in Delphi-made DLL using handles only. The form appears in host application normally, but the frame doesn't appear at all.
What could be wrong?
Below I provide a piece of code that creates both Frame and Window:
library DLL1;
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
uses
System.SysUtils,
System.Classes,
DllMain in 'DllMain.pas',
Winapi.Windows,
Vcl.Forms,
Vcl.Controls {DLLFrame1: TFrame},
DllForm in 'DllForm.pas' {Form1};
{$R *.res}
type
TSingleton = class
private
fra: TDLLFrame1;
frm: TForm1;
class var __instance: TSingleton;
class function __getInstance(): TSingleton; static;
public
class property Instance: TSingleton read __getInstance;
procedure CreateDLLFrame(AppHandle, ParentWindow: HWND);
procedure CreateDLLForm(AppHandle, ParentWindow: HWND);
procedure DestroyDLLFrame();
procedure DestroyDLLForm();
end;
procedure CreateDLLFrame(AppHandle, ParentWindow: HWND); stdcall;
begin
TSingleton.Instance.CreateDLLFrame(AppHandle, ParentWindow);
end;
procedure CreateDLLForm(AppHandle, ParentWindow: HWND); stdcall;
begin
TSingleton.Instance.CreateDLLForm(AppHandle, ParentWindow);
end;
procedure DestroyDLLFrame(); stdcall;
begin
TSingleton.Instance.DestroyDLLFrame();
end;
procedure DestroyDLLForm(); stdcall;
begin
TSingleton.Instance.DestroyDLLForm();
end;
exports
CreateDLLFrame,
CreateDLLForm,
DestroyDLLFrame,
DestroyDLLForm;
procedure TSingleton.CreateDLLFrame(AppHandle, ParentWindow: HWND);
begin
Application.Handle := AppHandle;
fra := TDLLFrame1.CreateParented(ParentWindow);
fra.Show();
end;
procedure TSingleton.DestroyDLLForm();
begin
frm.Free();
end;
procedure TSingleton.DestroyDLLFrame();
begin
fra.Free();
end;
procedure TSingleton.CreateDLLForm(AppHandle, ParentWindow: HWND);
begin
Application.Handle := AppHandle;
frm := TForm1.CreateParented(ParentWindow);
frm.Show();
end;
class function TSingleton.__getInstance(): TSingleton;
begin
if __instance = nil then
__instance := TSingleton.Create();
Result := __instance;
end;
end.
The DLLFrame:
unit DllMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TDLLFrame1 = class(TFrame)
mmoText: TMemo;
pnlSend: TPanel;
edtSend: TEdit;
btnSend: TButton;
private
public
constructor Create(AOwner: TComponent); override;
end;
implementation
{$R *.dfm}
{ TDLLFrame1 }
constructor TDLLFrame1.Create(AOwner: TComponent);
begin
inherited;
if AOwner = nil then
MessageBox(0, 'Frame owner is NIL', 'Debug', 0)
else
MessageBox(0, PWideChar(AOwner.Name), 'Debug', 0);
end;
end.
Delphi TFrame descend from TWinControl (and thus, TControl), they have an Owner and they have a Parent (often these are the same). The Owner controls the Frame's lifetime while the Parent controls where it's displayed (i.e. which Window handle is to be used). For example, in a VCL app with 2 form units and a frame unit, you could instantiate a Frame having it's owner be the Application object or the the first Form while having it's parent be the second form; the Frame would be displayed on the second form even though it's owner was the first frame.
What is the difference between Owner and Parent of a control?
This little example doesn't use DLLs, but it shows how the frame won't be displayed without a Parent being assigned:
unit CreateFrameAtRunTimeForm;
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)
Label1: TLabel;
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
uses CreateFrameAtRunTimeFrame;
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
var
F : TFrame3;
begin
F := TFrame3.Create(self);
F.Name := 'Frame'+Random(1000000).ToString;
F.Panel1.Caption := 'Frame '+F.Name;
F.Left := 200;
F.Top := 100;
end;
procedure TForm2.Button2Click(Sender: TObject);
var
F : TFrame3;
begin
F := TFrame3.Create(self);
F.Name := 'Frame'+Random(1000000).ToString;
F.Panel1.Caption := 'Frame '+F.Name;
F.Left := 200;
F.Top := 100;
F.Parent := self;
end;
end.
I'm sure your problem is that the Frame doesn't have a Parent control and I don't think it's possible to set one if you are only passing window handles around.
Being new to OOP, I'm curious why Delphi XE7 generated an invalid pointer operation on a logging class I was using whenI try to free it. So I created a simple test to create an object and then free it. I'm not sure what I'm missing here and why it throws this exception when MyObject.Free is called.
In the first unit, I create an instance of this object as shown here.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Unit2;
type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
MyObject: TMyObject;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
MyObject := TMyObject.Create;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
MyObject.Free;
end;
end.
In the 2nd unit, I have the object defined as follows.
unit Unit2;
interface
uses System.Classes;
type
TMyObject = class
public
constructor Create;
destructor Free;
end;
implementation
constructor TMyObject.Create;
begin
inherited Create;
end;
destructor TMyObject.Free;
begin
inherited Free;
end;
end.
Any help is appreciated.
Always implement a destructor by overriding the virtual destructor named Destroy.
type
TMyObject = class
public
constructor Create;
destructor Destroy; override;
end;
constructor TMyObject.Create;
begin
inherited;
end;
destructor TMyObject.Destroy;
begin
inherited;
end;
To destroy an instance call the method named Free in TObject. This calls the virtual destructor Destroy only if the instance is not nil.
Learn more from the documentation:
http://docwiki.embarcadero.com/Libraries/en/System.TObject.Free
http://docwiki.embarcadero.com/Libraries/en/System.TObject.Destroy
The name MyObject is weak. Object is used for instances. Class is used for classes.
I have problem with host application, which loads DLL form and interfaceing some function and properties.
The purpose is load a dll, show name as module name, set connection to ADOTable component and show form with data. Everything is working fine. But after close the host app a host app crashed and I get windows that hostapp.exe stopped working.
I do not know whether it is by freeing library or setting nil for interface.
Do you have any solution? Thanks.
Interface CODE
unit u_baseplugin_intf;
interface
uses Data.Win.ADODB, Data.DB;
type
IBaseModuleInterface = interface
['{060A9C46-B3CF-4BA4-B025-2DC1D9F45076}']
function GetModuleName: Ansistring;stdcall;
procedure SetConn(sConn:TAdoConnection);stdcall;
procedure showF;stdcall;
procedure freeF;stdcall;
property ModuleName: Ansistring read GetModuleName;
property Connection : TAdoConnection write SetConn;
end;
implementation
end.
DLL code
library profileslist;
uses
System.SysUtils,
System.Classes,
u_baseplugin_intf,
u_profileslist in 'u_profileslist.pas' {Form_DLL};
{$R *.res}
function LoadModule:IBaseModuleInterface;stdcall;
begin
result:=TForm_DLL.Create(nil);
end;
exports
LoadModule;
begin
end.
DLL Form code
unit u_profileslist;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.DBGrids, Vcl.StdCtrls,
u_baseplugin_intf, Data.DB,Data.Win.ADODB;
type
TForm_DLL = class(TForm, IBaseModuleInterface)
DBGrid1: TDBGrid;
ADOTable1: TADOTable;
DataSource1: TDataSource;
procedure FormShow(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
{Interface methods implementation}
function GetModuleName: AnsiString;stdcall;
procedure SetConn(sConn:TAdoConnection);stdcall;
public
{ Public declarations }
{Interface methods implementation}
procedure ShowF;stdcall;
procedure FreeF;stdcall;
end;
var
Form_DLL: TForm_DLL;
implementation
{$R *.dfm}
{Interface methods implementation}
function TForm_DLL.GetModuleName;
begin
Result := 'Profiles list';
end;
procedure TForm_DLL.SetConn(sConn: TAdoConnection);
begin
AdoTable1.Connection:=sConn;
end;
procedure TForm_DLL.ShowF;
begin
ShowModal;
end;
procedure TForm_DLL.FreeF;
begin
FreeAndNil(Form_DLL);
end;
{Form_DLL methods implementation}
procedure TForm_DLL.FormClose(Sender: TObject; var Action: TCloseAction);
begin
AdoTable1.Active:=false;
end;
procedure TForm_DLL.FormShow(Sender: TObject);
begin
AdoTable1.Active:=true;
end;
end.
HOST app code
program hostapp;
uses
Vcl.Forms,
u_hostapp in 'u_hostapp.pas' {Form1},
u_baseplugin_intf in 'u_baseplugin_intf.pas';
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Host app FORM code
unit u_hostapp;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
u_baseplugin_intf,
Data.Win.ADODB, Data.DB;
type
TForm1 = class(TForm)
ADOConnection1: TADOConnection;
Button1: TButton;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TModuleInterface = function:IBaseModuleInterface; stdcall;
var
Form1: TForm1;
implementation
{$R *.dfm}
var
aModuleIntf : IBaseModuleInterface;
dllHandle : cardinal;
procedure LoadModule( aLibName : pWideChar );
var
lModule : TModuleInterface;
begin
dllHandle := LoadLibrary(aLibName) ;
if dllHandle <> 0 then
begin
#lModule := GetProcAddress(dllHandle, 'LoadModule') ;
if Assigned (lModule) then
aModuleIntf := lModule //call the function
else
begin
ShowMessage('GetModuleIntf not found.') ;
FreeLibrary(dllHandle) ;
end;
end
else
begin
ShowMessage(aLibName+' not found.') ;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
aModuleIntf.Connection:=AdoConnection1;
aModuleIntf.ShowF;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
aModuleIntf.Connection:=nil;
aModuleIntf.freeF;
aModuleIntf:=nil;
FreeLibrary(dllHandle);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
LoadModule('profileslist.dll');
Label1.Caption:=aModuleIntf.ModuleName;
end;
end.
You never assign to Form_DLL. This means that when you call FreeF, you then perform FreeAndNil(Form_DLL). Since Form_DLL is nil, this does nothing, and the form still exists.
Fix that by changing LoadModule:
function LoadModule:IBaseModuleInterface;stdcall;
begin
Assert(not Assigned(Form_DLL));
Form_DLL:=TForm_DLL.Create(nil);
result:=Form_DLL;
end;
Although, I'd probably change the design completely by removing Form_DLL altogether. The host app maintains a reference to the form, on which the call to Free can be made. In other words, remove Form_DLL and implement FreeF like this:
procedure TForm_DLL.FreeF;
begin
Free; // or Destroy
end;
Or even better, use reference counted interfaces on the implementing object and let aModuleIntf:=nil take the form down.