How to find a dangling interface that causes an AV in Delphi
I've a complex application to which I've just introduced some changes, addi开发者_开发知识库ng a couple of new classes with interfaces and deleting some others. Functionally it all works but I get an access violation just after the Destroy procedure of a class:
"Access violation at address 0040B984 in module 'xxxx.exe'. Read of address 80808088".
I know this to be in the 'Finalize' code of the class and sure enough if I step into the dissassembly (Delphi 2010) I can see the point of the AV. I cannot see an easy way to find out which of my variables is triggering this though. Is there a procedure to be followed when going this deep that would get me a clue to the instance that is being referred-to?
Thanks Brian
This error looks like you are using FastMM for memory management.
The error indicates that you are referring a pointer that has been cleared by FastMM with the DebugFillDWord
value.
It means that you are using an interface that references to an object that has already been freed.
It also means you have not enabled CatchUseOfFreedInterfaces
.
In order to change those, and to debug, you cannot do with the stock FastMM that comes with Delphi.
You will need to download FastMM (version 4.94).
After download:
Like gabr already mentions, inside FastMM4Options.inc
, make sure you enable FullDebugMode
and CatchUseOfFreedInterfaces
(which disables CheckUseOfFreedBlocksOnShutdown
, but you are not interested in the latter right now).
You might want to enable RawStackTraces
as well; that depends if your current stack trace is good enough.
When you have done these settings, then run your app with FastMM through the debugger and put a breakpoint on this method inside the FastMM4 unit:
procedure TFreedObject.InterfaceError;
I have modified my FastMM4 unit a bit to get me more context info; I can share that with you (I have already mailed it to the FastMM4 team, but it has not been included in the official sources yet).
I wrote a pretty dense blog article on debugging using FastMM that might help you.
Drop a note here if that needs further explanation :-)
Good luck, and let us know if you need further directions.
--jeroen
Edit: 20100701 - emphasised the bits mentioned in Brian's comment.
In most cases such errors can be caught by using FastMM and compiling application with conditional defines FullDebugMode and CatchUseOfFreedInterfaces. Just make sure that you put FastMM4 in the first place in the dpr's 'uses' list.
Steps to find the problem:
- Use FastMM in fulldebugmode as Gabr suggested (I think you already do, looking at the 808080 pattern).
- Set all interfaces you use in your class explicitly to nil in your Destroy procedure
- Put a breakpoint at the start of your Destroy procedure
- Now step through your Destroy procedure, when nilling the dangling interface you will get your Access Violation and you will know which interface it was.
- When you still have the AV after you nilled all your interfaces without a problem, do step 2 - 5 for the parent class.
I've had these problems too and the above method helped me find them. My problems were caused by TComponents that implemented interfaces. Say you have ComponentA and ComponentB, ComponentB implements an interface. You assign ComponentB (or its interface) to ComponentA and store the interface reference. Now ComponentB gets destroyed, but ComponentA doesn't know about that. When you destroy ComponentA it nils the interface, calls the _Release method and you get the AV.
The solution to that is to work with TComponent.FreeNotification. When you receive a free notification from ComponentB, you nil the interface in ComponentA. I don't know anything about your code, but if your problem is similar you can work with FreeNotifications too.
Edit: added step 5
A similar bug which has bitten me was a interface reference which has been set on an existing object, the interface reference counter will not decrease automatically when the owner object is freed. It can be solved with an if Assigned(FMyInterface) then FMyInterface := nil;
in the owner object's destructor.
I do a similar thing, and the following code in the destructor of your object will help
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;
Then you can at least find out where you are freeing the object innapropriately. Probably you are freeing it too early. Freeing the object too early will not actually cause a problem, it is when you are freeing the object that holds the interface reference to the already freed object that you will get the error.
The other thing you can do is put breakpoints in your addref and release methods and trace who is keeping the interface references and if those same object are freeing them afterwards.
Also a common problem is as follows, if you get an interface and free the object in the same method
var
o:TSomeObject;
begin
o:=TSomeObject.Create;
(o as ISomeInterface).DoSomething;
o.free
end;
This will cause an AV at the end of the method, because the compiler creates a fake interface variable that is freed at the end of the method.
you would need to do this
var
o:TSomeObject;
i:ISomeInterface;
begin
o:=TSomeObject.Create;
i:=(o as ISomeInterface); // or Supports or whatever
i.DoSomething;
i:=nil;
o.free
end;
One thing to look for in your code is this
FInterfacedObject.GetInterface
in the same scope as
FInterfacedObject := TInterfacedObjectClass.Create.
where FInterfacedObject is a class variable.
You can calll GetInterface from a inner function if you want to, but if you call GetInterface in the same scope as you created FInterfacedObject, for whatever reason you'll drop the reference count to 0 and free the thing, but it won't be nil, so if you you do
if assigned(FInterfacedObject) then
FInterfacedObject.Free;
you'll get an access violation.
May be with a tool like EurekaLog ? http://delphi.about.com/od/productreviews/ss/eurekalog.htm
精彩评论