【问题标题】:Strange AV when storing an Delphi interface reference存储 Delphi 接口引用时的奇怪 AV
【发布时间】:2012-06-14 20:53:42
【问题描述】:

我在以下代码中遇到意外的访问冲突错误:

program Project65;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  SysUtils;

type
  ITest = interface
  end;

  TTest = class(TInterfacedObject, ITest)
  end;

var
  p: ^ITest;

begin
  GetMem(p, SizeOf(ITest)); 
  p^ := TTest.Create; // AV here
  try
  finally
    p^ := nil;
    FreeMem(p);
  end;
end.

我知道接口应该以不同的方式使用。但是,我正在研究使用这种方法的遗留代码库。而且我很惊讶地看到保留 SizeOf(ITest) 内存来放置 ITest 是不够的。

现在有趣的是,如果我将第一行更改为

GetMem(p, 21);

比 AV 没了。 (20 字节或更少失败)。对此有何解释?

(我使用的是 Delphi XE2 Update 4 + HotFix)

请不要评论代码有多糟糕或建议如何正确编码。请回答为什么需要保留 21 个字节而不是 SizeOf(ITest) = 4?

【问题讨论】:

  • 你的代码看起来很奇怪。为什么需要 "^ITest" 和 GetMem/FreeMem 对? TTest 是 TInterfacedObject 的后代,所以 p 应该只是一个 ITest。它是引用计数的,所以当它超出范围时会自动销毁。无需使用 GetMem/FreeMem。
  • 这是使用接口的完全错误的方式。您能解释一下您希望完成什么,以便有人可以为您指明更好的方向吗?
  • 1. AV = Access Violation 2. 我知道不应该这样使用。我试图了解我必须使用的遗留代码,这是一种常见的做法。请问各位cmets这段代码有多糟糕,而是尝试回答这个问题?
  • @Ken,我认为您的分析不正确。有时将接口分配给指针以获取弱引用。在此示例中,引用计数正常工作。当接口分配给 p^ 时它会增加,当 p^ 设置为 nil 时它会减少(此时对象被销毁)。
  • 我不同意,但我会把这个留给可能比我解释得更好的人。 :-) 很高兴您处理的是遗留代码而不是我。祝你好运。

标签: delphi pointers interface access-violation


【解决方案1】:

您有效地编写的是在幕后执行以下逻辑:

var
  p: ^ITest;
begin
  GetMem(p, SizeOf(ITest));
  if p^ <> nil then p^._Release; // <-- AV here
  PInteger(p)^ := ITest(TTest.Create);
  p^._AddRef;
  ...
  if p^ <> nil then p^._Release;
  PInteger(p)^ := 0;
  FreeMem(p);
end;

GetMem() 不能保证将其分配的内容清零。当您将新对象实例分配给接口变量时,如果字节不为零,则 RTL 将认为已经存在一个接口引用并尝试调用其_Release() 方法,导致 AV 因为它不支持一个真实的对象实例。您需要事先将分配的字节清零,然后 RTL 将看到 nil 接口引用,并且不再尝试调用其 _Release() 方法:

program Project65;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  SysUtils;

type
  ITest = interface
  end;

  TTest = class(TInterfacedObject, ITest)
  end;

var              
  p: ^ITest;              

begin              
  GetMem(p, SizeOf(ITest));               
  try
    FillChar(p^, SizeOf(ITest), #0); // <-- add this!
    p^ := TTest.Create; // <-- no more AV
    try
      ...
    finally
      p^ := nil;
    end;
  finally
    FreeMem(p);
  end;
end.

【讨论】:

  • 或者使用 AllocMem 代替 GetMem+FreeMem
  • 更正:我的意思是 GetMem+FillChar。 AllocMem 仍然需要 FreeMem。
  • @Craig:确实,他们应该是首选。他们确实在必要时初始化为零。
猜你喜欢
  • 2011-03-09
  • 1970-01-01
  • 2014-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-27
  • 1970-01-01
相关资源
最近更新 更多