当前位置 : 主页 > 编程语言 > delphi >

delphi – 为什么这个TStreamAdapter没有发布?

来源:互联网 收集:自由互联 发布时间:2021-06-23
比较这两个片段: (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)); 这在第
比较这两个片段:

(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并且行为正常.为什么强制_Release调用需要一个明确的nil赋值?这是Delphi 7的已知问题吗?是因为链接器/编译器切换?

这是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
);

它按此顺序发生:

> TFileStream.Create.
> TStreamAdapter.Create.
> x._释放清除旧参考.
>参考新的IStream.

这显然是错误的顺序.在调用TFileStream.Create之前,需要清除x.

根据前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
网友评论