How to save an unknown Interface to a Pointer? - delphi

Can I save an unknown interface to a pointer ?
For example I have an unknown interface and want to save it to a TreeNode Data?
var
X : Inknown;
To save :
....
Node:=TreeView1.Items.Add;
//Node.data:=x; //compiler won't allow this
Node.data:=#x;
...
To get :
...
var
//X:=Node.data; //compiler won't allow this too
Pointer(X):=Node.data; //an exception caught
...

An interface is a pointer, so you can store it as-is (don't use the # operator). However, to ensure the interface's lifetime, you have to increment/decrement its reference count manually for as long as the node refers to it, eg:
Node := TreeView1.Items.Add;
Node.Data := Pointer(x);
x._AddRef;
x := IUnknown(Node.Data);
procedure TMyForm.TreeView1Deletion(Sender: TObject; Node: TTreeNode);
begin
IUnknown(Node.Data)._Release;
end;

You need to capture the value of the interface reference rather than the address of the variable that holds the reference.
So, do it like this:
Node.Data := Pointer(X);
....
X := IInterface(Node.Data);
// use IInterface in preference to IUnknown
But you must make sure that you handle the reference counting properly since this cast subverts automatic reference counting.
So, take a reference when you put the interface into the Node:
Node.Data := Pointer(X);
X._AddRef;
and call _Release whenever you modify Node.Data, or destroy the node.
This is rather messy, and easy to get wrong, as you found out. A better way to solve the problem is to let the compiler do the work. Define a sub-class of TTreeNode, and make your tree view use that sub-class. Ignore the Data property of the node and use your own property of type IInterface, introduced in the sub-class.
The way to make this happen is to supply an OnCreateNodeClass event handler that tells the tree view control to create nodes of your sub-class rather than plain old TTreeNode. And example of that technique can be found here: https://stackoverflow.com/a/25611921/

Related

Does this FreeAndNil(lAux) frees my double field too?

I'm correcting some (a lot) memory leaks of a project. I have some Delphi experience but is not my main language so I'm extra careful at freeing stuff.
I've already tried it and everything looks fine, but I thought that it would free the Field too (not what I want). Does that happen just with Objects?
TMainObject = class(TObject)
public
FDoubleField : double;
*
*
*
procedure TMainObject.CalculateSomeFieldValue();
var
lAux : TAuxObject;
begin
lAux := TAuxObject.Create;
lAux.RecoverData;
if **some condition** then
begin
FDoubleField := lAux.DoubleProperty;
end
else
begin
FDoubleField := lAux.OtherDoubleProperty;
end;
FreeAndNil(lAux);
end
Thanks in advance
Edit: I do not want to lose FDoubleField because I will use it later, but I need to free lAux.
The FDoubleField field doesn't need to be freed because it will be automatically "freed" as soon it goes out of scope.
Some tips:
You don't need to use FreeAndNil(lAux) in this case, just use lAux.Free because there's no need to set a pointer to nil which will no longer be used.
At the beginning, you can set ReportMemoryLeaksOnShutDown := True (documentation). In this way, you will be notified about any memory leak when the application closes.
Use try-finally blocks for being sure to always free the object you created
Modified code using try-finally block and Free:
procedure TMainObject.CalculateSomeFieldValue();
var
lAux : TAuxObject;
begin
lAux := TAuxObject.Create;
try
lAux.RecoverData;
if **some condition** then
begin
FDoubleField := lAux.DoubleProperty;
end
else
begin
FDoubleField := lAux.OtherDoubleProperty;
end;
finally
lAux.Free;
end;
end
In the example you give, you don't need to free anything for a variable of type double in a class (Also called a "field").
Field that must be freed are classes or simple pointers of any type that have been allocated.
The normal place to free data in a class is in the destructor.
Also pay attention to interfaces. They are generally reference counted and as such MUST NOT be freed. Frequently, interfaces are assigned a value by calling a class constructor and you must really look if the value returned by the class constructor is assigned to a variable of an interface type or to a variable of some object type. Assuming TAuxObject also implement the interface IAuxInterface, your have the following valid code:
var
AuxObj : TAuxObject;
AuxIntf : IAuxInterface;
begin
AuxIntf := TAuxObject.Create; // This one must NOT be freed
AuxObj := TAuxObject.Create; // This one must be freed
try
....
finally
AuxObj.FRee;
end;
end;
Properly using interfaces is a big subject. There are cases where interfaces are not reference counted.
There are in Delphi libraries a few classes designed to be ancestor for object supporting interface (For example TInterfacedObject). This ancestor implement reference counting properly (Methods QueryInterface, _AddRef and _Release). If your object do no derive from a class already implementing reference counting, then if you need reference counting then you must implement methods QueryInterface, _AddRef and _Release. As soon as they are defined, some compiler magic will call them when it is needed. And by the way freeing the object must actually done by the _Release method implementation.
Things becomes even more complex if the developer make use of weak and unsafe keywords to mark the interface type variable.

Delphi release interface pointer

I'm struggling with interfaces in Delphi. This question might be trivial, but I am new to Delphi, so please excuse.
I have a TreeView with customized nodes, which hold an interface to an object (essentially, just like it is proposed here: Storing interface pointer inside tree view nodes).
The problem is, once I delete a node (in order to redraw the treeview) and set the interface variable to nil (freeing won't do with interfaces for some reason I haven't fully understood), the weirdest thing happens:
In my object, which contains a list, an integer and a string variable, the string and list will be set empty, while the integer remains the same.
I can't explain this. Does anybody know a workaround, or the possible reason for this behavior? BTW, I am using Delphi 10.2 Tokyo.
Here's my quite unspectacular destroy method:
myNode.destroy;
begin
intf:= nil;// intf holds the interface to the object
end;
Edit: this is a simplified version of my code:
The object I'm referring to: (I have several similar classes which look like obj but are slightly different and I don't know which one will be stored in the interface, but all share these variables)
Obj = class(InterfacedObject, IMyinterface)
count: integer; //this remains the same
children: array of ChildObj; //this will be emptied
name: string; //this will be set to ''
procedure addChild;
procedure IMyInterface.add = addChild;
end;
My customized treeNode:
MyNode = class(TTreeNode)
Intf: IMyinterface;
destructor destroy; override;
end;
Inside my class manages the TreeView:
MyForm.ReloadTree;
begin
if myTreeView.Items.Count > 0 then
begin
myTreeView.Items.Clear;
end
for I:= 0 to RootObj.Count-1 do
begin
myTreeView.Items.AddChild(MyTreeview.Items[0], RootObj.Children[i].name);
(myTreeView.Items[0][i] as MyNode).Intf := Intf(RootObj.Children[i]);
//I will proceed iterating over all children and their children, doing
//the same process, a level higher in the treeView
//...
end;
end;
in my object, which contains a list, an integer and a string variable, the string and list will be set empty, while the integer remains the same.
This is perfectly normal behavior. Strings and interfaces are compiler-managed types. Integers are not. When an object is destructed, compiler-managed data members are automatically deallocated as needed, which in the case of strings and interfaces involves nil'ing the pointers to their referenced data. The containing object itself is not zeroed out completely, so non-managed types, like integers, are not overwritten in memory.
Now, that being said, I see some bugs in your ReloadTree() procedure.
Your for loop is exceeding the upper bound of the RootObj.Children[] list.
When calling AddChild(), the second parameter is a string. You are passing RootObj.Children[i] in that parameter. But, in the next statement, you are type-casting the same RootObj.Children[i] value to an interface when assigning the MyNode.Intf field. A string is not an interface. So, what exactly does RootObj.Children[] contain - strings or interfaces?
When assigning the MyNode.Intf field, you are always accessing the first node in the TreeView, instead of the newly added node.

Delphi TDictionary : values are replace automatically when have the same key

I am using delphi XE5.
This a resume of my code, Next code works, but there have to be something in my code that destroy normal behaviour:
unit Class1;
type
TClass1 = class
private
FDic:TDictionary<String,String>.Create;
public
constructor create;
procedure insertValue(key,value:String);
end;
implementation
constructor TClass1.create;
begin
FDic:=TDictionary<String,String>.Create;
end;
procedure insertValue(key,value:String);
begin
if(FDic.ContainsKey(key))then
FDic[key] := value
else
begin
FDic.Add(key,value);
end;
end.
And now another unit:
unit Class2;
type
uses Class2;
TClass1 = class
public
class2 :TClass2;
TS: TStringList;
procedure DoSomething;
end;
implementation
procedure TClass1.DoSomething;
var
i: Integer;
c,test: TClass1;
begin
c := TClass1.create;
c.insertValue('height','.34cm');
c.insertValue('width','22cm');
c.insertValue('radio','2cm');
TS.AddObject('square',c);
c := TClass1.create;
c.insertValue('height','.88cm');
c.insertValue('width','11cm');
c.insertValue('top','12cm');
TS.AddObject('circle',c);
test := TS.Objects[0] as TClass1;//test.FDic height should be .34cm but gets the value of the last object create, the same for width an common keys.
//here when I look for the values of FDic test.FDic.Items['height'] the value is .88cm instead of .34cm, each time the values of any element of the Dictionary is replace with the previous of the last object created. And the memory address is the same. Why don't create a new memory address for any new element if it is a different object.
That is a resume of my code, I can put all my code because is too big, but I would like to know where I can search to resolve this problem. I am not is not easy, maybe I am not the only one with that problema, maybe some class in the uses, class variables, there is something that causes a memory problema in that dictionary, but no way to find it.
It's a little hard to be sure of the problem because you posted code that does not compile. For future reference, please don't do that. It's good to cut down the code to a small size, but you should then make it into a small console application that compiles and runs and demonstrates the fault. In spite of this, I think that I can see where the problem is.
You are creating objects and then adding them to a string list with
TS.AddObject(...);
But then you never free those objects. That, I guess, is the source of the leak. You can deal with this by setting the OwnsObjects property of the string list to True.
Specifies whether the string list owns the objects it contains.
The OwnsObjects property specifies whether the string list owns the
stored objects or not. If the OwnsObjects property is set to True,
then the Destroy destructor will free up the memory allocated for
those objects.
That I think will explain the leaks. The other part of your question is why index 0 contains the item that you added second. The only explanation for that, given the code in the question, is that your string list has the Sorted property set to True.
Again, I'm inferring this with a little intuition, but if you have only posted a complete program that compiled and run then your question could have been answered with complete certainty.
I do wonder whether or not a string list is the correct class to be using here. Perhaps you would be better with TObjectDictionary<string, TClass1>. You would create it like this:
dict := TObjectDictionary<string, TClass1>.Create([doOwnsValues]);
The use of doOwnsValues tells the dictionary that it is take assume ownership of the objects that you add as values and destroy them when they are removed from the dictionary, or when the dictionary is destroyed.
Note also that your insertValue method can be implemented in a far simpler way using the AddOrSetValue method.

Access Violation when handling forms

I have procedure to show/hide one element on TForm like that:
procedure ShowHideControl(const ParentForm: TForm; const ControlName: String; ShowControl: Boolean);
var
i: Integer;
begin
for i := 0 to pred(ParentForm.ComponentCount) do
begin
if (ParentForm.Components[i].Name = ControlName) then
begin
if ShowControl then
TControl(ParentForm.Components[i]).Show
else
TControl(ParentForm.Components[i]).Hide;
Break;
end;
end;
end;
then I try to use it like:
procedure TForm1.Button6Click(Sender: TObject);
begin
ShowHideEveryControl(TForm(TForm1), 'Button4', True);
end;
Why do I get Access Violation on Button6 click?
For me everything is OK... Button4 exists as a child :)
This cast is wrong:
TForm(TForm1)
You are telling the compiler to ignore the fact that TForm1 is not a TForm instance, and asking it to pretend that it is. That is fine until you actually try to use it as an instance, and then the error occurs.
You need to pass a real instance to a TForm descendent. You can write it like this:
ShowHideEveryThing(Self, 'Button4', True);
Just in case you are not clear on this, the parameter of your procedure is of type TForm. That means you need to supply an instance of a class that either is, or derives from TForm. I repeat, you must supply an instance. But you supply TForm1 which is a class.
And then the next problem comes here:
if (ParentForm.Components[i].Name = FormName) then
begin
if ShowForm then
TForm(ParentForm.Components[i]).Show
else
TForm(ParentForm.Components[i]).Hide;
Break;
end;
Again you have used an erroneous cast. When the compiler tells you that it a particular object does not have a method, you must listen to it. It's no good telling the compiler to shut up and pretend that an object of one type is really an object of a different type. Your button is categorically not a form, so don't try to cast it to TForm.
It is very hard to know what you are actually trying to do here. When you write:
ShowHideEveryThing(Self, 'Button4', True);
It would seem to me to me more sensible to write:
Button4.Show;
It is not a good idea to refer to controls using their names represented as text. It is much safer and cleaner to refer to them using reference variables. That way you let the compiler do its job and check the type safety of your program.
The names used in your function are suspect:
procedure ShowHideEveryThing(const ParentForm: TForm; const FormName: String;
ShowForm: Boolean);
Let's look at them:
ShowHideEveryThing: but you claim that the function should show/hide one element. That does not tally with the use of everything.
FormName: you actually pass a component name, and then look for components owned by ParentForm that have that name. It seems that FormName is wrong.
ShowForm: again, do you want to control visibility of the form, or a single element?
Clearly you need to step back and be clear on the intent of this function.
As an aside, you should generally never need to write:
if b then
Control.Show
else
Control.Hide;
Instead you can write:
Control.Visible := b;
My number one piece of advice to you though is to stop casting until you understand it properly. Once you understand it properly, design your code if at all possible so that you don't need to cast. If you ever really do need to cast, make sure that your cast is valid, ideally by using a checked cast with the as operator. Or at least testing first with the is operator.
Your code shows all the hallmarks of a classic mistake. The compiler objects to the code that you write. You have learnt from somewhere that casting can be used to suppress these compiler errors and now you apply this technique widely as a means to make your program compile. The problem is that the compiler invariably knows what it is talking about. When it objects, listen to it. If ever you find yourself suppressing a compiler error with a cast, take a step back and think carefully about what you are doing. The compiler is your friend.

How to set a record field as 'Procedure of object' before an object exists so that it can run

Very un-snappy title I know.
I have a series of text lines that I need to perform certain operations on in a certain order. I have come up with a means of doing this by defining the following record structure:
TProcessOrderRecord = record
RecordTypes: TByteSet;
InitialiseProcedure: TPreScanProc;
ProcessProcedure: TProcessRecord;
FinaliseProcedure: TEndScanProc;
end;
AProcessOrderArray = array of TProcessOrderRecord;
Initialise tends to call a constructor which will fill a field in the host object.
Process will be a procedure on the object which will be called for each text line that matches one of the record types in RecordTypes.
Finalise will tend to call the destructor and possibly do any checks when it knows that the full set of records has been processed.
The means of processing this array is quite straightforward:
procedure TImport.ScanTransferFile;
var
i: integer;
lArrayToProcess: AProcessOrderArray;
begin
lArrayToProcess := SetUpProcessingOrder(NLPGApp.ImportType);
for i := low(lArrayToProcess) to high(lArrayToProcess) do
begin
ProcessRecordType(lArrayToProcess[i].RecordTypes, lArrayToProcess[i].InitialiseProcedure, lArrayToProcess[i].ProcessProcedure, lArrayToProcess[i].FinaliseProcedure);
end;
end;
procedure TImport.ProcessRecordType(const RecordTypesToFind: TByteSet; PreScanProcedure: TPreScanProc; OnFindRecord: TProcessRecord; OnCompleteScan: TEndScanProc);
var
lLineOfText: string;
lIntegerRecordID: byte;
begin
if Assigned(PreScanProcedure) then PreScanProcedure;
try
if assigned(OnFindRecord) then
begin
Reader.GoToStartOfFile;
while not Reader.EndOfFile do
begin
lLineOfText := Reader.ReadLine;
lIntegerRecordID := StrToIntDef(GetRecordID(lLineOfText), 0);
if lIntegerRecordID in RecordTypesToFind then
begin
try
OnFindRecord(lLineOfText);
except
on E: MyAppException do
begin
// either raise to exit or log and carry on
end;
end;
end;
end;
end;
finally
// OnCompleteScan usually contains calls to destructors, so ensure it's called
if Assigned(OnCompleteScan) then OnCompleteScan;
end;
end;
My problem is that I want to define a record as such:
RecordTypes = [10]
InitialiseProcedure = ProcToCreateFMyObj
ProcessProcedure = FMyObj.do
FinaliseProcedure = ProcToFreeFMyObj
This compiles fine, however when ProcessProcedure is called, as FMyObj was nil when the ProcessProcedure is set, the instance of TMyObj is nil even though FMyObj is now set. Is there any clean way to get the record to point to the instance of FMyObj at the time of calling rather than at the time of first assignment?
At present I have resorted to having 'caller' methods on the host object which can then call the FMyObj instance when needed, but this is creating quite a bloated object with lots of single-line methods.
Edit to clarify/complicate the problem
Sometimes one instance of FObj can handle more than one types of record (usually if they have a master-detail relationship). In this case, InitialiseProcedure of the first record type will create FObj, FinaliseProcedure of the second record will free FObj and each record's ProcessProcedure can reference different procedures of FObj (do1 and do2).
At present I have resorted to having 'caller' methods on the host object which can then call the FMyObj instance when needed, but this is creating quite a bloated object with lots of single-line methods.
That is the right solution. Since the instance is not available at the point of initialisation you have no alternative.
When you use of object you are defining something called a method pointer. When you assign to a variable of method pointer type, the instance is captured at the point of assignment. There is no mechanism for the instance associated with a method pointer to be dynamically resolved. The only way to achieve that is to use runtime delegation, which is what you are currently doing. As is so often the case, another layer of indirection is used to solve a problem!
Your record that contains a number of methods looks awfully like an interface. I suspect that the most elegant solution will involve an interface. Perhaps at the point of calling you can call a function that returns an interface. And that function will using the value of FMyObj at the time of calling to locate the appropriate interface.
Yes it is possible to make additional runtime initialization of your record:
var
A: TProcessOrderRecord;
begin
..
TMethod(A.ProcessProcedure).Data:= FMyObj;
..
end;
though I would prefer a different solution, like the one you already use.

Resources