tags:

views:

134

answers:

3

I have a main app and a type library contains 2 COM objects, one is IFile, one is IFiles. IFiles creates IFile, and stores them in a TLIST, and has standard methods like Add, Remove etc. Both IFile and IFiles are TAutoObject.

"Add" method in IFiles is working fine, it simply creates IFile object [Code 1], and adds it to TList. Problem is IFile object instance gets lost in a very strange way. see [Code 2]

[Code 1]

function IFiles.Add(AFilename: String): IFile;
begin
  Result := CoIFile.Create;
  Result.Filename := AFilename;
  // ShowMessage(IntToStr(Result._AddRef));
  fFiles.Add(@Result);
end;

In the main app I have test code like this. [Code 2]

var
  i: Integer;
  f: IFile;
  Files: IFiles;
begin
  Files := CoTIFile.Create;
  for i:= 1 to 4 do
  begin
    // Create a dummy file object
    f := Files.Add('Filename ' + IntToStr(i));
    f._AddRef; // Not sure if AddRef works like this
    // Prints out the last file
    Memo1.Lines.Add(Files.Files[i-1].Filename);
  end;

  for i:= 0 to Files.Count-1 do
  begin
    f := Files.Files[i];
    // F is nil at all time.
    if (f<>nil) then Memo1.Lines.Add(f.Filename); // ! No print out.
  end;  
end;

From the 2nd loop, even though fFiles.Count = 4, but all contents have lost. Do I need some extra treatment in IFile to handle AddRef and Release? or the way IFiles.Add method I wrote is wrong?

A: 

The COM objects i automaticly released if there is no references to it. In Code 1 the COM object gets released at the "end" statement.

I think you need to creat a wrapper object, and that wrapper object is what you add to files.

In sory i don't have the time to creat an example right now.

BennyBechDk
+7  A: 

Try using tinterfacelist rather than TList to store the instances of IFile. This may solve your problem.

Toby Allen
Aha. TInterfaceList.
Darkerstar
+1  A: 

The problem in your original code was that you were adding an IFile pointer to the list, but when you read a value out of the list later, you assigned the pointer directly to another IFile variable. So you had was was essentially a PIFile value stored in an IFile variable. Delphi generally allows you to assign the untyped Pointer type to any pointer-like type, including interfaces.

To fix your original code, you would need to write the second look something like this:

var
  p: Pointer;

for i := 0 to Pred(Files.Count) do begin
  p := Files.Files[i];
  if not Assigned(p) then
    continue;
  f := IFile(p^);
  if not Assigned(f) then
    continue;
  Memo1.Lines.Add(f.Filename);
end;

You were right to call f._AddRef in your first loop. When IFiles.Add returns, the reference count on the result is 1 because the value stored in the loop is a pointer, not the actual reference. You need to increment the reference count because f is going to be re-used for other values. Since the reference you're manually counting is stored in the FFiles list, it would be better to call _AddRef inside IFiles.Add instead of waiting until it returns.

When you clear the list, or as you remove items from the list, you would need to call _Release on all the interface references.

But Toby's answer gives the better idea: Use TInterfaceList to store a list of interfaces. TList simply isn't suited to the task by itself.

A final piece of advice: The "I" prefix on names is used to denote interface types. Interfaces don't have method implementations of their own. You've shown the implementation of IFiles.Add, so IFiles clearly isn't an interface type. It should be named TFiles instead, or maybe TFileList.

Rob Kennedy