1

I have an interfaced object defined thusly (a list of TStringList):

type
  IMyList = IArrayList<TStringList>;
  TMyList = TArrayList<TStringList>;

I create instances to this in a function and return them to the caller. Here I get such an instance then put save it into a TListBox.Items.Objects[] property for later reference, then display it. The idea is when someone clicks on the TListBox entry, I want to get this list from Objects[] and display it.

procedure do_something;
var
  a_list : IMyList;
begin
  . . .
  a_list := get_some_data_in_a_list();
  // (1)
  listbox1.Items.Objects[n] := TMyList(a_list); // save ref in Items.Objects
  ShowList( a_list );
  . . .
end;

Here's the onClick handler for the listbox:

procedure TfrmMain.listbox1Click(Sender: TObject);
var
  n : integer;
  a_list : IMyList;
begin
  n := listbox1.ItemIndex;
  if (n < 0) then exit;
  a_list := IMyList(listbox1.Items.Objects[n]);  // (2)

  ShowList( listbox1.Items[n], a_list );
end;

The problem is, at line (2) above, a_list is NIL. The interface is destructing the object after it's added to the Objects[] property.

If I add this at line (1) above:

a_list._AddRef;

then at line (2) a_list shows up just as expected.

This seems like a hack that shouldn't be necessary, and it's probably caused by the cast to add the item into Objects[], which is required because its type is IMyList which isn't derived from TObject, whereas TMyList is.

How can I make this work properly without resorting to this hack?

David Schwartz
  • 1,756
  • 13
  • 18
  • 1
    I just recently asked an almost identical question: http://stackoverflow.com/questions/25611118/storing-interface-pointer-inside-tree-view-nodes however I'm not sure if I'd consider it a duplicate. – Jerry Dodge Sep 04 '14 at 18:13
  • Yes, it looks like the same issue. Thanks for pointing it out. I'll try a similar solution. – David Schwartz Sep 04 '14 at 18:25
  • Don't forget, if you do use the "hack" `a_list._AddRef` then make sure you decrement it using `a_list._Release` – Jerry Dodge Sep 04 '14 at 18:28
  • Yes, I did that in FormDestroy. – David Schwartz Sep 04 '14 at 18:32
  • The original hack is actually storing an interface as an object. That either requires the next hack, calling `_AddRef` (and later on, `_Release`), or the solution given to Jerry Dodge. FWIW, the solution given to Jerry is already implemented in other lists (even in the VCL, IIRC) for strings, even since Delphi 2. – Rudy Velthuis Sep 04 '14 at 20:39
  • Solve it the same way. Use a list view and handle OnCreateItemClass. – David Heffernan Sep 05 '14 at 06:11
  • 1
    Or even better would be to stop using the gui control as a container. Absolutely no need at all to put anything into `Objects[]`. Maintain a proper container and present a view of that from your gui. – David Heffernan Sep 05 '14 at 08:01
  • @DavidHeffernan: Indeed, that is the best solution, but that would probably require a redesign. Although, I think it is OK to keep GUI stuff like icons, glyphs, colours, or similar information in the `Objects[]`. I assume that was the original purpose anyway. – Rudy Velthuis Sep 05 '14 at 13:16

1 Answers1

0

Thanks to @Jerry Dodge, this is the same issue he was having with TTreeView.Data property.

I created a simple wrapper class to encapsulate a_list as an IMyList and saved that in Objects[] instead.

In the TListBox.OnClick handler, it comes out of Objects[] just fine, and I can access the wrapped value with no problem. It is not being destroyed this way.

In the wrapper's destructor, I simply assigned NIL to the a_list value and I have no further problems.

David Schwartz
  • 1,756
  • 13
  • 18