This VCL Form program generates the Invalid Pointer Operation notice:
Uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls,
DcadMenu_u;
type
TForm1 = class(TForm)
MenuTestRichEdit: TRichEdit;
LoadButton: TButton;
procedure ButtonLoadClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.ButtonLoadClick(Sender: TObject);
var
menu : TDcadMenu;
item1, item2 : TDcadMenuItem;
strlist :tstringlist;
i : integer;
begin
menu := tDcadMenu.Create();
item1 := TDcadMenuItem.create ('Option1', 'Do Option1', false, false, true);
menu.add (item1);
item2 := TDcadMenuItem.create ('Option2', 'Do Option2', false, false, true);
menu.add (item2);
strlist := tstringlist.Create;
Try
For i := 0 to Menu.Count - 1 DO
begin
item1 := menu.Items[i];
strlist.Add (Item1.lblset + ' | ' + Item1.lblmsg );
end;
Form1.MenuTestRichEdit.Lines := strlist;
finally
item1.free;
item2.Free;
menu.free;
strlist.Free;
end;
end;
The code works fine and generates the item list in the Richedit component. I suspect I am freeing an object that is already being handled, but not clear on what the cause is specifically. Can someone explain this?
We can't see the implementation of TDcadMenu, but normally adding items to a class gives the ownership of the items to that class, so there is no need to free the items outside of the class. As #Remy comments, it is normally safe to free them before before freeing the menu object, though.
In your code you are reassigning item1, and when freeing the items, Item1 and Item2 both shares the same instance as menu.Items[1]. This means that you have a double free, which gives your invalid pointer notice.
item1.free;
item2.Free; // <- Double free of same instance
Related
I need for some reason the codepage of the language set by the currently selected keyboard layout in the current process. (I use Win10 with per app language settings)
getThreadLocale does not change when UI language changes. It gives back the default locale of the process.
getProcessInformation/getThreadInformation does not contain any information about the current language/locale.
I think the chain of the needed information is:
selected language => matching locale => codepage
if I have the current locale id (matching to the selected language) then I can fetch its codepage by:
getLocaleInfoW( idLocale, LOCALE_IDEFAULTANSICODEPAGE, buff, buffSize );
Is(Are) there any winapi call(s) to get the information described above?
The TLabel caption sets to the CodePage associated with the current keyboard language by the TButton.OnClick event handler.
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm3 = class(TForm)
Label1: TLabel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
procedure TForm3.Button1Click(Sender: TObject);
var
tid : word;
lid : word;
ndxLocale, buffSize : integer;
localeName : string;
buff : pchar;
begin
tid := getCurrentThreadID;
lid := getKeyboardLayout( tid );
ndxLocale := languages.IndexOf( lid );
localeName := languages.LocaleName[ndxLocale];
buffSize := getLocaleInfoEx( pchar( localeName ), LOCALE_IDEFAULTANSICODEPAGE, NIL, 0 );
getMem( buff, buffSize*sizeOf(char) );
try
getLocaleInfoEx( pchar( localeName ), LOCALE_IDEFAULTANSICODEPAGE, buff, buffSize );
label1.caption := strPas( buff );
finally
freeMem( buff );
end;
end;
GetACP() returns "ansi" code page...lol, not really ansi, but that's what windows calls it. Can also use GetCPInfo() to get additional information after you call GetACP(). Things get trickier for Japanese, Chinese, and other far east languages that use double byte character set. I still work on an application that is MBCS. Would be nice if we could convert to Unicode, but it's not happening and it won't be my problem soon.
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.
TComponent descendants (like TForm) disable interfaces reference counting, hence nobody is freeing the secondary form. The memory model is owner based, that is, when the parent object that owns an object is freed, it frees all it's children.
So, you could either pass an owner to the form on the factory function (maybe Application, or Application.MainForm) and adhere to TComponent's memory model or add a hook on the OnClose event of the form and set Action to caFree. The former will destroy the form when the application is closed, and the latter will destroy it as soon as the secondary form is closed (as soon as possible)
I want to show FirstName and LastName in a Combobox drop down.
The Problem is That I should work On 2 fields existing in a Table.
I checked the TDBlLookUpComboBox but the ListFields properties doesn't work for me in Delphi Xe.
I set The properties of TDBlLookUpComboBox to
DataSource :my datasource
ListFields : SUR_NAME; FIRST_NAME
Now, I am doing it by a basic way :
nameClient := Concat( sqlqry1.Fields.FieldByName('FIRST_NAME').AsString,' ',
sqlqry1.Fields.FieldByName('SUR_NAME').AsString);
cbbClient.Items.Add(nameClient);
Change the input
Just change the source data going into the DBCombo by putting in a query:
select concat(SUR_NAME,' ',FIRST_NAME) as NAME, * from mytable;
Now you can display the data in your combo box.
Make the output look nice in the combobox
Obviously you'll be using a TDBILookUpComboBox but everything else is the same.
In order to make multiple columns in the drop down you'll have to do your own drawing.
This can be done by changing the style to csOwnerDrawFixed and assigning the OnDrawItem event.
See the sample code below:
unit Unit18;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm18 = class(TForm)
ComboBox1: TComboBox;
procedure ComboBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
private
public
end;
var
Form18: TForm18;
implementation
uses
System.Types,
StrUtils;
{$R *.dfm}
procedure TForm18.ComboBox1DrawItem(Control: TWinControl; Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
Columns: TStringDynArray;
ColCount: Integer;
ItemText: string;
DC: HDC;
DrawRect: TRect;
Middle: integer;
begin
ItemText:= ComboBox1.Items[index];
Columns:= SplitString(ItemText, '|');
ColCount:= Length(Columns);
// For Demo purposes only the first 2 columns are displayed.
DC:= ComboBox1.Canvas.Handle;
Combobox1.Canvas.FillRect(Rect);
Middle:= Rect.Left + Rect.Width div 2;
Combobox1.Canvas.MoveTo(Middle, Rect.Top);
Combobox1.Canvas.LineTo(Middle, Rect.Bottom);
if ColCount > 0 then begin
DrawRect:= Rect;
OffsetRect(DrawRect,1,0);
DrawRect.Right:= DrawRect.Right - DrawRect.Width div 2;
DrawText(DC, Columns[0], Length(Columns[0]), DrawRect, 0);
end;
if ColCount > 1 then begin
DrawRect:= Rect;
OffsetRect(DrawRect,1,0);
DrawRect.Left:= DrawRect.Left + DrawRect.Width div 2;
DrawText(DC, Columns[1], Length(Columns[1]), DrawRect, 0);
end;
end;
end.
Put the following text in the Items:
test | test
line2 | part2
line 4 | part3
line 6
And this is what will be displayed:
Currently when i click on a button it will create some shapes on a new form. Once i close the new form how can i destroy the shapes it made.
I can add more info if needed but was hopeing there was a simple way to destroy all TMachine instances when the form closed.
TMachine is a TShape Class
procedure TFLayout1.GetClick(Sender: TObject);
var
azone: string;
adept: string;
machine : TMachine;
begin
fdb.count := 0; //keeps track of number of machines in zone
azone := MyDataModule.fDB.GetZone(Name); //gets name of zone
adept := TButton(Sender).Name; //gets name of dept
fdeptlayout.ListBox1.Clear;
fdeptlayout.show;
with fdeptlayout.ADOQuery1 do
begin
sql.Clear;
sql.BeginUpdate;
sql.Add('SELECT');
sql.Add(' *');
sql.Add('FROM');
sql.Add(' `MList`');
sql.Add('WHERE `Zone` = :myzone ');
sql.Add(' AND `Dept` = :mydept');
sql.EndUpdate;
parameters.ParamByName('myzone').Value := azone;
parameters.ParamByName('mydept').Value := adept;
open;
end;
//gets number of machines in total
while not fdeptlayout.ADOQuery1.Eof do
begin
fdb.count := fdb.count+1;
fdeptlayout.ADOQuery1.Next;
end;
//restarts back at first query
fdeptlayout.ADOQuery1.First;
//clears the last x value
fdb.LastX :=0;
//creates the shape
while not fdeptlayout.ADOQuery1.Eof do
begin
machine := MachineShape.TMachine.Create(self);
machine.Parent := fdeptlayout;
machine.PlaceShape(44,44,'CM402','first','123/33/123');
fdeptlayout.ListBox1.Items.Add(fdeptlayout.ADOQuery1.FieldByName('Name').AsString);
fdeptlayout.ADOQuery1.Next;
end;
end;
TMachine Class
unit MachineShape;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, extctrls,myDataModule,Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TMachine = class(TShape)
private
{ Private declarations }
public
{ Public declarations }
procedure PlaceShape(sizeW,sizeH :integer; name, order,asset : string);
end;
implementation
Procedure TMachine.PlaceShape(sizeW,sizeH :integer; name, order,asset : string);
begin
self.width := sizeW;
self.height := sizeH;
self.top := 136;
self.left := MyDataModule.fDB.LastX +2;//set left
MyDataModule.fDB.lastx := left + sizeW;
showmessage(inttostr(mydatamodule.fDB.LastX));
end;
end.
FDeptLayout
unit DeptLayout;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls,mydatamodule, Vcl.Forms, Vcl.Dialogs, Data.DB, Data.Win.ADODB, Vcl.StdCtrls,
Vcl.ExtCtrls;
type
TfDeptLayout = class(TForm)
ADOQuery1: TADOQuery;
ListBox1: TListBox;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
fDeptLayout: TfDeptLayout;
implementation
{$R *.dfm}
procedure TfDeptLayout.FormClose(Sender: TObject; var Action: TCloseAction);
begin
end;
end.
The shown code is taking advantage of the VCL ownership model and the form will free it for you, as you just pass the form itself as the owner of your components when you create it:
machine := MachineShape.TMachine.Create(self);
as this is called from the TFLayout1 class, when the particular instance of the form is destroying itself, it will free all the owned components.
For a little more info, you can read the article: Owner vs. Parent in Delphi.
Edit
From comments, it resulted you create the TMachine instances on a class different of the form on which you show it, and you don't destroy the form instance when you close it, so, you can reach what you want making this changes:
Make the form in which the shapes are shown the owner, changing your code to create them to this:
//don't use self, now the parent is the instance referenced by fdeptlayout
machine := MachineShape.TMachine.Create(fdeptlayout);
On your Tfdeptlayout class, add a OnClose handler with this code:
begin
for I := ComponentCount - 1 downto 0 do
if Components[I] is TMachine then
Components[I].Free;
end;
That said, you really have to read the documentation and referenced articles to gain some understanding of what's going on behind the scenes in your Delphi application.
You are assigning an Owner to your TMachine objects. The shapes will be freed automatically when the Owner itself is freed.
Assuming TFLayout1 is your Form class, then by default it will not be freed automatically when it is closed. A closed Form is hidden by default so you can re-show when needed. To actually free it on close, you have to either set the Action parameter in the TForm.OnClose event to caFree, or call TForm.Free() directly sometime after the form is closed (such as if you are displaying the Form with ShowModal(), then you can call Free() after ShowModal() exits).
If you want to free the shapes yourself without relying on the behavior of an Owner, then set the Owner to nil when you create the shapes, and store your TMachine pointers in a TList that you can loop through when needed to free each shape, or a TObjectList with its OwnsObjects property set to true that you can Clear() when needed. Such as in the Form's OnClose event.
I am using Embarcadero RAD Studio XE2 Update 4 and the Indy package shipped with it.
My intention is to find a server in LAN with broadcast from a TIdUDPClient that waits for a response from the server to get its IP. Receiving the data works fine if I use the TIdUDPClient method ReceiveString with no arguments.
But when I try to use the overloaded version found in the Indy 10 Documentation version 10.5.8.3 coming with RAD Studio, it does not compile and shows 'E2250: There is no overloaded version of 'ReceiveString' that can be called with these arguments'.
Here is my code:
unit Client;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdBaseComponent, IdComponent, IdUDPBase,
IdUDPClient, Vcl.StdCtrls, IdGlobal;
type
TFormLC = class(TForm)
UDPClient: TIdUDPClient;
LServer: TLabel;
Label2: TLabel;
Label3: TLabel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
FormLC: TFormLC;
implementation
{$R *.dfm}
function findServer:string;
var ans, ip : string;
port: TIdPort;
begin
with FormLC.UDPClient do begin
Active := True;
BroadcastEnabled:=True;
Broadcast('ServerRequest', 1234);
ans := ReceiveString(ip, port);
Active := False;
end;
if SameText(ans, 'ServerAccept') then
result := ip
else
result := '';
end;
procedure TFormLC.Button1Click(Sender: TObject);
var ans:string;
begin
LServer.Caption := findServer;
end;
end.
I noticed that the online documentation of Indy differs from the documentation that comes with the IDE and tried it as described there, without succes.
Any help would be great!
Your issue is caused by the with statement, you are passing the port property of the TIdUDPClient instead of the local variable port to the ReceiveString method.
function findServer:string;
var ans, ip : string;
port: TIdPort;
begin
with FormLC.UDPClient do begin
....
ans := ReceiveString(ip, port);//here you are passing the port property
Active := False;
end;
....
end;
As workaround rename you port local variable like so :
function findServer:string;
var ans, ip : string;
vport: TIdPort;
begin
with FormLC.UDPClient do begin
....
ans := ReceiveString(ip, vport);//now will work
Active := False;
end;
end;
or even better don't use the with statement.
TIdUDPClient has 2 overloads for ReceiveString():
function ReceiveString(const AMSec: Integer = IdTimeoutDefault; AByteEncoding: TIdTextEncoding = nil{$IFDEF STRING_IS_ANSI}; ADestEncoding: TIdTextEncoding = nil{$ENDIF}): string; overload;
function ReceiveString(var VPeerIP: string; var VPeerPort: TIdPort; const AMSec: Integer = IdTimeoutDefault; AByteEncoding: TIdTextEncoding = nil{$IFDEF STRING_IS_ANSI}; ADestEncoding: TIdTextEncoding = nil{$ENDIF}): string; overload;
When you call ReceiveString() without parameters, you are calling the first overload. When trying to call the second overload, your code fails to compile because your with statement is passing the TIdUDPClient.Port property to the second parameter, instead of your local port variable. The compile will not allow you to pass a property to a var parameter.
You need to remove the with statement and/or rename your port variable to resolve the conflict.