为什么这个TStreamAdapter没有发布?

时间:2014-04-03 18:46:36

标签: delphi com automatic-ref-counting

比较这两个片段:

(d as IPersistStream).Save(
  TStreamAdapter.Create(
    TFileStream.Create('test.bin',fmCreate),soOwned),true);
(d as IPersistStream).Load(
  TStreamAdapter.Create(
    TFileStream.Create('test.bin',fmOpenRead),soOwned));

第二个TFileStream.Create失败,因为第一个没有被销毁。这很奇怪,因为参数有唯一的引用,我认为它会在关闭Save调用时被破坏。所以我尝试了这个:

var
  x:IStream;
begin
  x:=TStreamAdapter.Create(
    TFileStream.Create('test.bin',fmCreate),soOwned);
  (d as IPersistStream).Save(x,true);
  x:=nil;
  x:=TStreamAdapter.Create(
    TFileStream.Create('test.bin',fmOpenRead),soOwned);
  (d as IPersistStream).Load(x);
  x:=nil;

哪个工作正常。 (但如果没有x:=nil;则再次失败)所以不要担心d IPersistStream并且行为正确。为什么要使用明确的nil分配来强制_Release来电?这是Delphi 7的已知问题吗?是因为链接器/编译器切换?

1 个答案:

答案 0 :(得分:5)

以下是IPersistStream.Save的声明:

function Save(const stm: IStream; fClearDirty: BOOL): HResult; stdcall;

关键是stream参数传递为const。这意味着Save函数不会引用IStream接口。它的引用计数既不递增也不递减。既然都没有发生,它永远不会被摧毁。

解决它的方法是确保某些东西包含对接口的引用。这是你在第二个例子中演示的内容。

您需要分配到nil的原因取决于执行此代码的顺序:

x := TStreamAdapter.Create(
  TFileStream.Create('test.bin',fmOpenRead),soOwned
);

按顺序发生:

  1. TFileStream.Create
  2. TStreamAdapter.Create
  3. x._Release清除旧参考。
  4. 参考新的IStream
  5. 这显然是错误的顺序。在致电x之前,您需要先清除TFileStream.Create


    根据前Embarcadero编译工程师Barry Kelly的说法,the issue regarding the interface passed to a const parameter is a bug。它从来没有被修复过,我一个人已经放弃了这种情况的希望。

    我的SSCCE来证明这个问题在这里:

    program SO22846335;
    
    {$APPTYPE CONSOLE}
    
    type
      TMyInterfaceObject = class(TObject, IInterface)
        FRefCount: Integer;
        FName: string;
        function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
        function _AddRef: Integer; stdcall;
        function _Release: Integer; stdcall;
        constructor Create(const Name: string);
        destructor Destroy; override;
      end;
    
    constructor TMyInterfaceObject.Create(const Name: string);
    begin
      inherited Create;
      FName := Name;
      Writeln(FName + ' created');
    end;
    
    destructor TMyInterfaceObject.Destroy;
    begin
      Writeln(FName + ' destroyed');
      inherited;
    end;
    
    function TMyInterfaceObject.QueryInterface(const IID: TGUID; out Obj): HResult;
    begin
      Result := E_NOINTERFACE;
    end;
    
    function TMyInterfaceObject._AddRef: Integer;
    begin
      Writeln(FName + ' _AddRef');
      Result := AtomicIncrement(FRefCount);
    end;
    
    function TMyInterfaceObject._Release: Integer;
    begin
      Writeln(FName + ' _Release');
      Result := AtomicDecrement(FRefCount);
      if Result = 0 then
        Destroy;
    end;
    
    procedure Foo(const Intf: IInterface);
    begin
      Writeln('Foo');
    end;
    
    procedure Bar(Intf: IInterface);
    begin
      Writeln('Bar');
    end;
    
    begin
      Foo(TMyInterfaceObject.Create('Instance1'));
      Bar(TMyInterfaceObject.Create('Instance2'));
      Readln;
    end.
    

    <强>输出

    Instance1 created
    Foo
    Instance2 created
    Instance2 _AddRef
    Bar
    Instance2 _Release
    Instance2 destroyed