Storing an interface pointer inside tree nodes

I am trying to store interface pointers as a tree in TTreeNode.Data properties. Although I can store the interface pointer ( Node.Data := Pointer(MyInterface); ), it doesn't seem to work the other way around ( MyInterface := ISomeInterface(Node.Data); ). He always comes out nil .

I also tried using manual reference counting, as I said, in another question . However, it still exits nil and now gives memory leaks.

 //Clears tree view and adds drive letters procedure TfrmMain.cmdRefreshBrowseClick(Sender: TObject); var Arr, O: ISuperObject; X: Integer; N, C: TTreeNode; begin //First clear all items and release their interface refs for N in tvBrowse.Items do begin O:= ISuperObject(N.Data); O._Release; end; tvBrowse.Items.Clear; Arr:= ListDirectory(''); //Returns ISuperObject array listing drives for X := 0 to Arr.AsArray.Length-1 do begin O:= Arr.AsArray.O[X]; N:= tvBrowse.Items.Add(nil, OS['drive']+':\ ['+OS['type']+']'); //Add root node N.Data:= Pointer(O); // Assign interface pointer to node data O._AddRef; //Manually increment interface reference count C:= tvBrowse.Items.AddChild(N, ''); //Add a fake child node end; end; procedure TfrmMain.tvBrowseExpanding(Sender: TObject; Node: TTreeNode; var AllowExpansion: Boolean); var N, C: TTreeNode; P, A, O: ISuperObject; X: Integer; begin //Check first node if it a fake node N:= Node.getFirstChild; if N.Text = '' then begin //if first node is a fake node... P:= ISuperObject(Node.Data); // <-- P always comes out nil here??? N.Delete; //Delete first "fake" node //Get child files/folders if Node.Parent = nil then //If root (drive) node... A:= ListDirectory(PS['drive']+':\') //Returns ISuperObject array listing files/folders else A:= ListDirectory(PS['name']); //Returns ISuperObject array listing files/folders for X := 0 to A.AsArray.Length-1 do begin O:= A.AsArray.O[X]; C:= tvBrowse.Items.AddChild(N, OS['name']); //Add child node C.Data:= Pointer(O); //Assign interface pointer to node data O._AddRef; //Manually increment reference count end; end; end; 

How can this be done?

+3
source share
1 answer

Essentially, you are doing it right. Your drives are reasonable, and you understand the need to do manual reference counting, because you keep the link in a field like Pointer that doesn't do reference counting.

 P := ISuperObject(Node.Data); 

If P to nil , this means that Node.Data is nil . Nothing more to say. Presumably, there is some pretty mundane reason for Data be nil , but that has nothing to do with how you cast.

Looking at my code, I would criticize it for confusing all the different problems. You can easily find this task if you can maintain a certain degree of isolation between different aspects.

One way to make life much easier is to avoid using the untyped Data pointer. Instead, use a custom node type that can do the correct reference counting:

 type TMyTreeNode = class(TTreeNode) private FIntf: IInterface; property Intf: IInterface read FIntf write FIntf; end; 

You need to handle the OnCreateNodeClass event of the tree view to get a control to create the node class.

 procedure TForm1.TreeView1CreateNodeClass(Sender: TCustomTreeView; var NodeClass: TTreeNodeClass); begin NodeClass := TMyTreeNode; end; 

Now, whenever a tree view control creates an instance of node, it creates one of the TMyTreeNode types. Which has a field to contain your interface. I typed it as IInterface here, but you would use a more specific interface that would suit your needs. And, of course, you can add any possible function to your own node type.

The soft binding to this is that you need to pass the node links from TTreeNode (as returned by the underlying tree control) to TMyTreeNode in order to access the interface property. However, this binding is worth it, in my opinion, because you can correctly rely on the compiler for a controlled lifetime, and therefore forget about this aspect of the code. This allows you to focus on your program, rather than on a tedious template. Continuing the path that you are on now looks like a recipe for memory leaks and access violations. Get a compiler for managing things, and you can be sure you won't make such mistakes.

+6
source

All Articles