如何在Delphi中找到导致AV的悬空接口

时间:2010-06-29 09:19:29

标签: delphi debugging interface access-violation

我有一个复杂的应用程序,我刚刚介绍了一些更改,添加了几个带接口的新类,并删除了其他一些。在功能上它一切正常但我在类的Destroy过程之后得到了访问冲突:

“模块'xxxx.exe'中地址0040B984的访问冲突。读取地址80808088”。

我知道这是在该类的'Finalize'代码中,如果我进入反汇编(Delphi 2010),我确实可以看到AV的重点。我看不出一个简单的方法来找出我的哪个变量触发了这个。在深入了解这个过程时是否需要遵循一个程序,这会让我了解被引用的实例的线索?

由于 布赖恩

7 个答案:

答案 0 :(得分:13)

在大多数情况下,可以使用FastMM并使用条件定义 FullDebugMode CatchUseOfFreedInterfaces 编译应用程序来捕获此类错误。只需确保将FastMM4放在dpr的“使用”列表中的第一位。

答案 1 :(得分:13)

此错误看起来像您正在使用FastMM进行内存管理。 该错误表示您正在引用已由FastMM清除并具有DebugFillDWord值的指针。

这意味着您正在使用一个引用已经被释放的对象的接口 这也意味着您尚未启用CatchUseOfFreedInterfaces

为了更改这些并进行调试,您无法使用Delphi附带的库存FastMM 您需要下载FastMM(版本4.94)。

下载后:

在[{1}}内已提及gabr,请确保启用FastMM4Options.incFullDebugMode(禁用CatchUseOfFreedInterfaces,但您对此不感兴趣后者)。
您可能还想启用CheckUseOfFreedBlocksOnShutdown;这取决于你当前的堆栈跟踪是否足够好。

当您完成这些设置后,然后通过调试器使用FastMM运行您的应用程序,并在FastMM4单元内的此方法上设置断点:

RawStackTraces

我已经修改了我的FastMM4单元以获得更多上下文信息;我可以与你分享(我已经将它邮寄给FastMM4团队,但它还没有包含在官方消息来源中)。

我写了一篇非常密集的blog article on debugging using FastMM来帮助你 如果需要进一步说明,请在此处留言: - )

祝你好运,如果你需要进一步的指示,请告诉我们。

- 的Jeroen

编辑:20100701 - 强调了Brian评论中提及的内容。

答案 2 :(得分:6)

找到问题的步骤:

  1. 像Gabr建议的那样在fulldebugmode中使用FastMM(我认为你已经这样做了,看看808080模式)。
  2. 在您的Destroy过程中将您在类中使用的所有接口显式设置为nil
  3. 在Destroy程序开始时设置断点
  4. 现在逐步执行您的销毁程序,当您悬挂悬空界面时,您将获得访问冲突,并且您将知道它是哪个界面。
  5. 如果您在没有问题的情况下填满所有接口后仍然拥有AV,请为父类执行步骤2 - 5。
  6. 我也有这些问题,上面的方法帮助我找到了它们。我的问题是由实现接口的TComponents引起的。假设您有ComponentA和ComponentB,ComponentB实现了一个接口。您将ComponentB(或其接口)分配给ComponentA并存储接口引用。现在ComponentB被破坏,但ComponentA不知道这一点。当您销毁ComponentA时,它会触发接口,调用_Release方法然后获得AV。

    解决方法是使用TComponent.FreeNotification。当您收到ComponentB的免费通知时,您将在ComponentA中找不到该界面。我对您的代码一无所知,但如果您的问题类似,您也可以使用FreeNotifications。

    编辑添加了第5步

答案 3 :(得分:3)

一个类似的错误,它是一个已经在现有对象上设置的接口引用,当释放所有者对象时,接口引用计数器不会自动减少。它可以在所有者对象的析构函数中使用if Assigned(FMyInterface) then FMyInterface := nil;来解决。

答案 4 :(得分:3)

我做了类似的事情,对象的析构函数中的以下代码将有所帮助

Destructor TMyObjectThatIAmDoingManualRefCounting.Destroy;
begin
  if FMyRefCount<>0 then
    messageDlg('You dork, you called Free on me when someone still had references to me');

  inherited;
end;

然后你至少可以找到你在哪里释放物体。可能你太早解放了它。过早释放对象实际上不会导致问题,当你释放持有对已经释放的对象的接口引用的对象时,你将得到错误。

你可以做的另一件事是在你的addref和release方法中放置断点,并跟踪谁保持接口引用,以及那些相同的对象是否在之后释放它们。

如果你得到一个接口并以相同的方法释放对象

,那么常见的问题如下所示
var
  o:TSomeObject;
begin
  o:=TSomeObject.Create;
  (o as ISomeInterface).DoSomething;
  o.free
end;

这会在方法结束时导致AV,因为编译器会创建一个在方法结束时释放的伪接口变量。

你需要这样做

var
  o:TSomeObject;
  i:ISomeInterface;
begin
  o:=TSomeObject.Create;
  i:=(o as ISomeInterface); // or Supports or whatever
  i.DoSomething;
  i:=nil;
  o.free
end;

答案 5 :(得分:2)

您的代码中需要注意的一件事是

FInterfacedObject.GetInterface 

的范围相同
FInterfacedObject := TInterfacedObjectClass.Create.

其中FInterfacedObject是一个类变量。

如果你愿意,你可以从内部函数中调用GetInterface,但如果你在创建FInterfacedObject的同一范围内调用GetInterface,无论出于什么原因你都会将引用计数降为0并释放该东西,但它赢了不要,所以如果你这样做

if assigned(FInterfacedObject) then
    FInterfacedObject.Free;

您将获得访问冲突。

答案 6 :(得分:2)

可能使用像EurekaLog这样的工具吗? http://delphi.about.com/od/productreviews/ss/eurekalog.htm