Using Delphi 10.4.1 I tried Custom Managed record management to initialize a record but still get memory leaks.
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, System.IOUtils, System.DateUtils, System.Character,
Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls;
type
TForm3 = class(TForm)
Button1: TButton;
RadioGroup1: TRadioGroup;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
type
TMyRec = record
DateTime: TDateTime;
v, size: integer;
str: string;
class operator Initialize (out Dest: TMyRec);
end;
TMyREcHolder = class
data: TMyRec;
constructor Create(const e: TMyRec);
end;
TMyList = class(TList)
procedure Clear; override;
end;
implementation
{$R *.dfm}
class operator TmyRec.Initialize (out Dest: TMyRec);
begin
Dest.str := '';
end;
{ TMyREcHolder }
constructor TMyREcHolder.Create(const e: TMyRec);
begin
inherited Create;
data := e;
end;
{ TMyList }
procedure TMyList.Clear;
var
i: integer;
begin
for i := 0 to Count - 1 do
TMyREcHolder(Items[i]).Free;
inherited Clear;
end;
procedure TForm3.Button1Click(Sender: TObject);
var
lst: TMyList;
i: integer;
rec: TMyRec;
FI: TSearchrec;
begin
Initialize(rec);
lst := TMyList.Create;
try
if FindFirst(TPath.Combine('C:\temp', '*.txt'), faAnyFile, FI) = 0 then
begin
repeat
if (FI.FindData.dwFileAttributes and faDirectory = 0) and
(FI.FindData.dwFileAttributes and faArchive = faArchive) then
begin
Application.ProcessMessages;
case RadioGroup1.ItemIndex of
0: Initialize(rec);
1: rec.str := '';
2: fillchar(rec, sizeof(rec), 0);
end;
try
rec.DateTime := FI.TimeStamp;
except
rec.DateTime := EncodeDateDay(1970, 1);
end;
rec.size := FI.size;
rec.str := FI.Name;
lst.Add(TMyREcHolder.Create(rec));
end;
until (FindNext(FI) <> 0);
FindClose(FI);
end;
finally
lst.Free;
end;
end;
end.
The radiogroup offers three items, both 1 and 3 leak memory. Can anyone explain why the Initialize one does? I want a reliable way of clearing a record to help me wean myself off a 20 year fillchar habit.
Related
I am using Python4Delphi and try to get the demo WrapDelphiDemo running.
What it should do is to calculate the amount of prime numbers for values up to 1000000.
The expected value is 78498 but when I let the demo code running I get 575843.
I found out that parameter value "MaxN" of the function is always a fix value of 8574564 instead the expected 1000000.
class function TDelphiFunctions.count_primes(MaxN: integer): integer;
var
Count : integer;
begin
Count := 0;
ShowMessage(format('function parameter MaxN=%d is WRONG!!!! Should be 1000000!!!',[MaxN]));
//MaxN := 1000000;
TParallel.&For(2, MaxN, procedure(i: integer)
begin
if IsPrime(i) then
AtomicIncrement(Count);
end);
Result := Count;
end;
I use Delphi Seattle with Win7.
Python4Delphi is the latest from GitHub.
I use the original demo code.
What I need to adapt is that with Seattle version I cannot use inline variable definition.
Does anyone have an idea what I can do?
Here the full code of MainForm:
unit MainForm;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, SynEdit, Vcl.StdCtrls,
PythonEngine, PythonGUIInputOutput, SynEditPythonBehaviour,
SynEditHighlighter, SynEditCodeFolding, SynHighlighterPython, Vcl.ExtCtrls,
WrapDelphi;
type
TForm1 = class(TForm)
sePythonCode: TSynEdit;
HeaderControl1: THeaderControl;
Panel1: TPanel;
Splitter1: TSplitter;
Panel2: TPanel;
HeaderControl2: THeaderControl;
mePythonOutput: TMemo;
SynPythonSyn: TSynPythonSyn;
SynEditPythonBehaviour: TSynEditPythonBehaviour;
PythonEngine: TPythonEngine;
PythonGUIInputOutput: TPythonGUIInputOutput;
btnRun: TButton;
PyDelphiWrapper: TPyDelphiWrapper;
PythonModule: TPythonModule;
procedure FormCreate(Sender: TObject);
procedure btnRunClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
System.Rtti,
System.Threading,
System.Math;
type
TDelphiFunctions = record
class function count_primes(MaxN: integer): integer; static;
end;
var
DelphiFunctions: TDelphiFunctions;
procedure TForm1.FormCreate(Sender: TObject);
var
Py : PPyObject;
begin
Py := PyDelphiWrapper.WrapRecord(#DelphiFunctions, TRttiContext.Create.GetType(TypeInfo(TDelphiFunctions)) as TRttiStructuredType);
PythonModule.SetVar('delphi_functions', Py);
PythonEngine.Py_DecRef(Py);
end;
procedure TForm1.btnRunClick(Sender: TObject);
begin
GetPythonEngine.ExecString(UTF8Encode(sePythonCode.Text));
end;
function IsPrime(x: Integer): Boolean;
var
q, i : integer;
begin
if (x <= 1) then Exit(False);
q := Floor(Sqrt(x));
for i := 2 to q do
if (x mod i = 0) then
Exit(False);
Exit(True);
end;
class function TDelphiFunctions.count_primes(MaxN: integer): integer;
var
Count : integer;
begin
Count := 0;
ShowMessage(format('function parameter MaxN=%d is WRONG!!!! Should be 1000000!!!',[MaxN]));
//MaxN := 1000000;
TParallel.&For(2, MaxN, procedure(i: integer)
begin
if IsPrime(i) then
AtomicIncrement(Count);
end);
Result := Count;
end;
end.
Hi guys I have an Error that appeared and that I cant get rid off..
I added 2 custom procedures to my delphi code and I read that you can hit crtl+shift+c to autogenerate the functions, which I did.
However my problem now is that I didnt need the autogenerated stuff thats why I deleted it after executing the command. Now my code does not work anymore because of this error I am getting:
E2029 Declaration expected but end of file found
Expected INITIALIZATION but recieved the end of file at line 520(520:1)
How can I fixx my code? Removing or adding a 'end' at the end of the file does not help me. Is there a way to find out where something is missing in my code? (I could post my delphi code but its 500lines long I dont think that makes sense.
Update Code:
unit Benutzerverwaltung_U;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.ComCtrls,
Vcl.StdCtrls,
Vcl.WinXCtrls, Vcl.CheckLst, System.Actions, Vcl.ActnList, Vcl.Menus,
System.StrUtils,
Data.DB, Vcl.Grids, Vcl.DBGrids, Vcl.DBCtrls, FireDAC.Stan.Intf,
FireDAC.Stan.Option, FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS,
FireDAC.Phys.Intf, FireDAC.DApt.Intf, FireDAC.Comp.DataSet,
FireDAC.Comp.Client;
type
TForm1 = class(TForm)
other buttons and so on...
procedure SwapValues(var Zahl1, Zahl2: Integer); //new
procedure SelectionSort(Sender: TObject); // new
procedure Button11Click(Sender: TObject); //new
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
workerModel: record
VorName: string[40];
NachName: string[40];
Age: Integer;
Schließen: string[30];
Admin: TToggleSwitchState;
DatenSehen: TToggleSwitchState;
Gender: string[20];
end;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses Sonderrechte_U, CheckedItem_U, Unit1, BenutzerEdit_u;
procedure TForm1.SwapValues(var Zahl1, Zahl2: Integer);
var
h: Integer;
begin
h := Zahl1;
Zahl1 := Zahl2;
Zahl2 := h;
end;
procedure TForm1.SelectionSort(Sender: TObject);
var
i, j, min: Integer;
var
sortArray, Data: Array of string;
begin
for i := 1 to Form1.ListBox1.Items.Count - 1 do
// i muss wahrscheinlich 0 sein?
begin
min := i;
for j := i + 1 to Form1.ListBox1.Items.Count do
if (Data[j] < Data[min]) then
min := j;
SwapValues(i, min);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
Form2 := TForm2.Create(Self);
try
Form2.ShowModal;
finally
Form2.Free;
end;
end;
// more code
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var
l: Integer;
t: String;
begin
with ListBox1 do
begin
Canvas.FillRect(Rect);
t := Items[Index];
l := Rect.Right - Canvas.TextWidth(t) - 1;
Canvas.TextOut(l, Rect.Top, t);
end;
end;
procedure TForm1.SearchBox1Change(Sender: TObject);
var
i: Integer;
begin
// SearchBox1.Parent := ListBox1;
ListBox1.Items.BeginUpdate;
try
for i := 0 to ListBox1.Items.Count - 1 do
ListBox1.Selected[i] := ContainsText(ListBox1.Items[i], SearchBox1.Text);
finally
ListBox1.Items.EndUpdate;
end;
// end;
// this is the end of the file
A Delphi unit must end with
end.
(notice the full stop).
I am trying to capture the Device ID of an AirCard. I am using the following code with the intentions of storing the results in a text file (imei.txt) that I store in the Temp folder and loop through the contents, looking for DEVICE ID.
The problems is that it only writes "The following command was not found: mbn show interface." to the file.
I have tested the Netsh command from the command line and it returns what I would expect.
xs1 := CreateOleObject('WSCript.Shell');
xs1.run('%comspec% /c netsh mbn show interface > "' + IMEIFileName +
'"', 0, true);
It is failing to process the NetSh command properly. Am I passing it through the Comspec correctly? It seems to not run the "NetSh" command and acts as if I am running "mbn" from the command prompt.
Thanks
unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Win.ComObj, ShlObj, Vcl.StdCtrls;
type
TfrmMain = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure GetAirCardInformation;
{ Private declarations }
public
{ Public declarations }
IMEI: string;
PhoneNumber: string;
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.Button1Click(Sender: TObject);
begin
GetAirCardInformation;
end;
procedure TfrmMain.GetAirCardInformation;
var
xs1 : OleVariant;
IMEIFileName: String;
IMEIStrings: TStringList;
I: Integer;
function GetSpecialFolder(const CSIDL: Integer): string;
var
RecPath: PWideChar;
begin
RecPath := StrAlloc(MAX_PATH);
try
FillChar(RecPath^, MAX_PATH, 0);
if SHGetSpecialFolderPath(0, RecPath, CSIDL, false) then
result := RecPath
else
result := '';
finally
StrDispose(RecPath);
end;
end;
begin
IMEI := '';
IMEIFileName := GetSpecialFolder(CSIDL_LOCAL_APPDATA) + '\Temp\imei.txt';
Memo1.Lines.Add('IMEIFileName: ' + IMEIFileName);
try
if FileExists(IMEIFileName) then
DeleteFile(IMEIFileName);
xs1 := CreateOleObject('WSCript.Shell');
xs1.run('%comspec% /c netsh mbn show interface > "' + IMEIFileName +
'"', 0, true);
if FileExists(IMEIFileName) then
begin
IMEIStrings := TStringList.Create;
IMEIStrings.LoadFromFile(IMEIFileName);
IMEIStrings.NameValueSeparator := ':';
Memo1.Lines.Add('IMEIStrings Count: ' + intToStr(IMEIStrings.Count));
for I := 0 to IMEIStrings.Count - 1 do
begin
Memo1.Lines.Add(IMEIStrings.text);
if (Uppercase(Trim(IMEIStrings.Names[I])) = 'DEVICE ID') then
begin
IMEI := Trim(IMEIStrings.Values[IMEIStrings.Names[I]]);
Memo1.Lines.Add('IMEI:' + IMEI);
break;
end;
end;
end;
except
IMEI := '';
end;
Memo1.Lines.Add('process complete');
end;
end.
You should not be using the WShell COM object to run cmd.exe. That is overkill. You can use CreateProcess() instead. However, when running cmd.exe programmably, you cannot redirect its output using the > operator, that only works in an actual command window. You can instead use the STARTUPINFO structure to redirect the output to an anonymous pipe created with CreatePipe(), and then you can read from that pipe using ReadFile(). No need to use a temp file at all. MSDN has an article on this topic:
Creating a Child Process with Redirected Input and Output
There are plenty of examples floating around that demonstrate this technique in Delphi.
That being said, a better option is to not use netsh at all. Windows 7 and later have a Mobile Broadband API. You can enumerate the MBN interfaces directly in your code.
For example, using the WwanEnumerateInterfaces() function:
unit WwApi;
{$MINENUMSIZE 4}
interface
uses
Windows;
const
WWAN_STR_DESC_LENGTH = 256;
type
WWAN_INTERFACE_STATE = (
WwanInterfaceStateNotReady,
WwanInterfaceStateDeviceLocked,
WwanInterfaceStateUserAccountNotActivated,
WwanInterfaceStateRegistered,
WwanInterfaceStateRegistering,
WwanInterfaceStateDeregistered,
WwanInterfaceStateAttached,
WwanInterfaceStateAttaching,
WwanInterfaceStateDetaching,
WwanInterfaceStateActivated,
WwanInterfaceStateActivating,
WwanInterfaceStateDeactivating
);
WWAN_INTF_OPCODE = (
WwanIntfOpcodePin,
WwanIntfOpcodeRadioState,
WwanIntfOpcodePreferredProviders,
WwanIntfOpcodeCurrentConnection,
WwanIntfOpcodeProvisionedContexts,
WwanIntfOpcodeActivateUserAccount,
WwanIntfOpcodeVendorSpecific,
WwanIntfOpcodeInterfaceObject,
WwanIntfOpcodeConnectionObject,
WwanIntfOpcodeAcState,
WwanIntfOpcodeClearManualConnectState,
WwanIntfOpcodeGetStoredRadioState,
WwanIntfOpcodeGetRadioInfo,
WwanIntfOpcodeHomeProvider
);
// I don't know the definition of this type!
WWAN_STATUS = DWORD; //?
WWAN_INTERFACE_STATUS = record
fInitialized: BOOL;
InterfaceState: WWAN_INTERFACE_STATE;
end;
PWWAN_INTERFACE_INFO = ^WWAN_INTERFACE_INFO;
WWAN_INTERFACE_INFO = record
InterfaceGuid: TGuid;
strInterfaceDescription: array[0..WWAN_STR_DESC_LENGTH-1] of WCHAR;
InterfaceStatus: WWAN_INTERFACE_STATUS;
ParentInterfaceGuid: TGuid;
fIsAdditionalPdpContextInterface: BOOL;
end;
PWWAN_INTERFACE_INFO_LIST = ^WWAN_INTERFACE_INFO_LIST;
WWAN_INTERFACE_INFO_LIST = record
dwNumberOfItems: DWORD;
pInterfaceInfo: array[0..0] of WWAN_INTERFACE_INFO;
end;
function WwanOpenHandle(dwClientVersion: DWORD; pReserved: Pointer; var pdwNegotiatedVersion: DWORD; var phClientHandle: THandle): DWORD; stdcall;
function WwanCloseHandle(hClientHandle: THandle; pReserved: Pointer): DWORD; stdcall;
function WwanEnumerateInterfaces(hClientHandle: THandle; pdwReserved: PDWORD; var ppInterfaceList: PWWAN_INTERFACE_INFO_LIST): DWORD; stdcall;
procedure WwanFreeMemory(pMem: Pointer); stdcall;
function WwanQueryInterface(hClientHandle: THandle; const pInterfaceGuid: TGuid; opCode: WWAN_INTF_OPCODE; pReserved: Pointer; var pdwDataSize: DWORD; var ppData: PByte; var pRequestId: ULONG; var pStatus: WWAN_STATUS): DWORD; stdcall;
implementation
const
WwApiLib = 'WwApi.dll';
function WwanOpenHandle; external WwApiLib delayed;
function WwanCloseHandle; external WwApiLib delayed;
function WwanEnumerateInterfaces; external WwApiLib delayed;
procedure WwanFreeMemory; external WwApiLib delayed;
function WwanQueryInterface; external WwApiLib delayed;
end.
unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TfrmMain = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure GetAirCardInformation;
{ Private declarations }
public
{ Public declarations }
IMEI: string;
PhoneNumber: string;
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
uses
WwApi;
procedure TfrmMain.Button1Click(Sender: TObject);
begin
GetAirCardInformation;
end;
procedure TfrmMain.GetAirCardInformation;
var
dwNegotiatedVersion: DWORD;
hClientHandle: THandle;
pInterfaceList: PWWAN_INTERFACE_INFO_LIST;
pInterface: PWWAN_INTERFACE_INFO;
I: DWORD;
begin
IMEI := '';
Memo1.Clear;
try
// The value of the first parameter is undocumented!
// WlanOpenHandle() has a similar parameter, where 1
// is for XP and 2 is for Vista+. Maybe it is the same
// for WwanOpenHandle()?...
//
if WwanOpenHandle(2, nil, dwNegotiatedVersion, hClientHandle) = 0 then
try
if WwanEnumerateInterfaces(hClientHandle, nil, pInterfaceList) = 0 then
try
Memo1.Lines.Add('IMEIStrings Count: ' + IntToStr(pInterfaceList.dwNumberOfItems));
if pInterfaceList.dwNumberOfItems > 0 then
begin
pInterface := #pInterfaceList.pInterfaceInfo[0];
for I := 0 to pInterfaceList.dwNumberOfItems-1 do
begin
// use pInterface as needed...
Memo1.Lines.Add('Desc:' + StrPas(pInterface.strInterfaceDescription));
Memo1.Lines.Add('Intf:' + GUIDToString(pInterface.InterfaceGuid));
// and so on ...
Memo1.Lines.Add('');
Inc(pInterface);
end;
end;
finally
WwanFreeMemory(pInterfaceList);
end;
finally
WwanCloseHandle(hClientHandle, nil);
end;
except
end;
Memo1.Lines.Add('process complete');
end;
end.
Alternatively, using the IMbnInterfaceManager and IMbnInterface COM interfaces, which give you more detailed information:
unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
type
TfrmMain = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure GetAirCardInformation;
{ Private declarations }
public
{ Public declarations }
IMEI: string;
PhoneNumber: string;
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
uses
// I found the MbnApi.pas unit on the DelphiPraxis forum:
//
// http://www.delphipraxis.net/1342330-post2.html
//
// It is too large to post here on StackOverflow!
// Otherwise, you can import the mbnapi.tlb TypeLibrary yourself...
//
MbnApi, ActiveX, ComObj;
procedure TfrmMain.Button1Click(Sender: TObject);
begin
GetAirCardInformation;
end;
procedure TfrmMain.GetAirCardInformation;
var
Mgr: IMbnInterfaceManager;
pInterfaceArray, pPhoneNumberArray: PSafeArray;
pInterface: IMbnInterface;
subscriber: IMbnSubscriberInformation;
ReadyState: MBN_READY_STATE;
lIntfLower, lIntfUpper: LONG;
lPhoneNumLower, lPhoneNumUpper: LONG;
I, J: LONG;
wStr: WideString;
begin
Memo1.Clear;
try
OleCheck(CoCreateInstance(CLASS_MbnInterfaceManager, nil, CLSCTX_ALL, IMbnInterfaceManager, Mgr));
OleCheck(Mgr.GetInterfaces(pInterfaceArray));
try
OleCheck(SafeArrayGetLBound(pInterfaceArray, 1, lIntfLower));
OleCheck(SafeArrayGetUBound(pInterfaceArray, 1, lIntfUpper));
for I = lIntfLower to lIntfUpper do
begin
OleCheck(SafeArrayGetElement(pInterfaceArray, I, pInterface));
try
// use pInterface as needed...
OleCheck(pInterface.get_InterfaceID(wStr));
try
Memo1.Lines.Add('Interface ID:' + wStr);
finally
wStr := '';
end;
OleCheck(pInterface.GetReadyState(ReadyState));
Memo1.Lines.Add('Ready State:' + IntToStr(Ord(ReadyState)));
OleCheck(pInterface.GetSubscriberInformation(subscriber));
try
OleCheck(subscriber.Get_SubscriberID(wStr));
try
Memo1.Lines.Add('Subscriber ID: ' + wStr);
finally
wStr := '';
end;
OleCheck(subscriber.Get_SimIccID(wStr));
try
Memo1.Lines.Add('Sim ICC ID: ' + wStr);
finally
wStr := '';
end;
OleCheck(subscriber.Get_TelephoneNumbers(pPhoneNumberArray));
try
OleCheck(SafeArrayGetLBound(pPhoneNumberArray, 1, lPhoneNumLower));
OleCheck(SafeArrayGetUBound(pPhoneNumberArray, 1, lPhoneNumUpper));
for J = lPhoneNumLower to lPhoneNumUpper do
begin
OleCheck(SafeArrayGetElement(pPhoneNumberArray, J, wStr));
try
Memo1.Lines.Add('Phone #:' + wStr);
finally
wStr := '';
end;
end;
finally
SafeArrayDestroy(pPhoneNumberArray);
end;
finally
subscriber := nil;
end;
// and so on...
Memo1.Lines.Add('');
finally
pInterface := nil;
end;
end;
finally
SafeArrayDestroy(pInterfaceArray);
end;
except
end;
Memo1.Lines.Add('process complete');
end;
end.
Step 1:
write an application with the code:
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, System.Generics.Collections,
FMX.Types, FMX.Graphics, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.StdCtrls;
type
TObjChild = class;
TObjTest = class
private
FName: string;
FChilds: TList<TObjChild>;
public
property Name: string read FName write FName;
property Childs: TList<TObjChild> read FChilds write FChilds;
constructor Create;
destructor Destroy; override;
end;
TObjChild = class
private
FAdress: string;
FPostalCode: string;
public
property Adress: string read FAdress write FAdress;
property PostalCode: string read FPostalCode write FPostalCode;
end;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
{ TObjTeste }
constructor TObjTest.Create;
begin
FChilds := TObjectList<TObjChild>.Create;
end;
destructor TObjTest.Destroy;
var
i: integer;
begin
for i := 0 to FChilds.count -1 do
begin
FChilds[I].Free;
end;
FreeAndNil(FChilds);
inherited;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
ListObjs: TList<TObjTest>;
lObjTeste: TObjTest;
lObjChild: TObjChild;
J: Integer;
begin
ListObjs := TList<TObjTest>.Create;
for I := 0 to 5000 do
begin
lObjTeste := TObjTest.Create;
for J := 0 to 2000 do
begin
lObjChild := TObjChild.Create;
lObjTeste.FChilds.Add(lObjChild)
end;
ListObjs.Add(lObjTeste);
end;
if MessageDlg('Delete objects?', TMsgDlgType.mtConfirmation, [TMsgDlgBtn.mbOK], 0) = idOK then
begin
for I := 0 To ListObjs.Count - 1
begin
ListObjs[I].Free;
end;
FreeAndNil(ListObjs);
end;
end;
end.
Step 2: Run application and press button1
After pressing the OK button messagedlg of the application does not release the memory
Step 3: Repeat steps sometimes the application returns a memory low
The problem is here:
constructor TObjTest.Create;
begin
FChilds := TObjectList<TObjChild>.Create;
end;
destructor TObjTest.Destroy;
var
i: integer;
begin
for i := 0 to FChilds.count - 1 do
begin
FChilds[i].Free;
end;
FreeAndNil(FChilds);
inherited;
end;
By default TObjectList<T> takes ownership of its members. So you do not need to, and indeed should not, free the members in the destructor.
So here:
for i := 0 to FChilds.count - 1 do
begin
FChilds[i].Free;
end;
you free the members. But then here:
FreeAndNil(FChilds);
The object list also frees the members. Who have already been freed. That double free leads to your runtime errors.
The fix is to remove the explicit freeing of the object list members and rely on the list to do the work:
destructor TObjTest.Destroy;
begin
FChilds.Free;
inherited;
end;
This ownership of its members is the single reason for the existence of TObjectList<T>. That is the only functionality that it offers beyond that provided by TList<T>. Read about it here: http://docwiki.embarcadero.com/Libraries/en/System.Generics.Collections.TObjectList
Finally, the plural of child is children.
I have a problem when my Form1 appear on body of browser window, simply he disappear when I put focus in my Form and the browser window lose focus. How solved it? Any suggestion will welcome.
See image below:
and here is my complete code:
Unit for enumeration of windows (EnumWindowUtil_.pas):
unit EnumWindowUtil_;
interface
uses
Winapi.Windows,
System.SysUtils,
System.Classes;
type
TWindowList = class(TStringList)
private
FAddClassname: Boolean;
public
procedure EnumChildWindows(handle: HWND);
property AddClassname: Boolean read FAddClassname write FAddClassname;
end;
var
wlistChilds: TWindowList;
implementation
function GetWindowClassName(hwnd: HWND): string;
begin
SetLength(Result, 1024);
GetClassName(hwnd, PChar(Result), Length(Result));
Result := PChar(Result);
end;
procedure EnumWindowCallback(hwnd: HWND; lParam: TWindowList); stdcall;
var
buffer: array[0..255] of char;
texto: string;
begin
if (not IsWindowVisible(hwnd)) then
Exit;
SendMessage(hwnd, $000D, 256, Integer(#buffer));
texto := StrPas(buffer);
texto := texto + ':' + GetWindowClassName(hwnd) + ' - ' + Format('%6.6x', [hwnd]) + '/' + IntToStr(hwnd);
lParam.AddObject(texto, TObject(hwnd));
end;
procedure TWindowList.EnumChildWindows(handle: HWND);
begin
Clear;
if Winapi.Windows.EnumChildWindows(handle, #EnumWindowCallback, Integer(Self)) then;
end;
end.
Here is main unit:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, EnumWindowUtil_,
Vcl.StdCtrls;
type
TForm1 = class(TForm)
Timer1: TTimer;
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
linha: string;
implementation
{$R *.dfm}
function GetNavigatorHandle(BASE: HWND): HWND;
var
I: integer;
begin
linha:= '';
Result := 0;
wlistChilds := TWindowList.Create;
wlistChilds.AddClassname := True;
wlistChilds.EnumChildWindows(BASE);
for I := 0 to wlistChilds.Count - 1 do
begin
linha := wlistChilds.Strings[I];
if
(Pos('Chrome_Render',linha)>0)then
begin
Result := StrToInt(copy(linha, pos('/', linha) + 1, Length(linha)));
Break;
end;
end;
FreeAndNil(wlistChilds);
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var
janela, janelaContainer: HWND;
begin
janela := GetForegroundWindow;
janelaContainer := GetNavigatorHandle(Janela);
if janelaContainer = 0 then
begin
Exit;
end;
Winapi.Windows.SetParent(form1.handle,janelaContainer);
end;
end.