Skip to content

Access Violation in _IntfClear

Ever see an access violation in _IntfClear (in System.pas)? Typically these come up when you’re closing a form or shutting down an app. Unless you build with Debug DCUs, you may not even recognize that the error is in _IntfClear. Note that this applies only to Delphi for Win32 — you’ll never see the problem in Delphi for .NET.

The problem is caused by using an interface on a class which does not implement reference counting. Notably, this is true of any TComponent subclass, TXMLDocument when it is created in certain ways, and any class where you’ve overridden _AddRef and _Release to return -1 instead of incrementing/decrementing a value.

With such a class the lifetime of instances is not managed by reference counting. In the case of a TComponent, for example, it’s usually managed by Ownership. However, due to the nature of interface references, _AddRef and _Release are still going to be called when the reference goes out of scope. If the class has been freed prior to that time, then you have an AV in _IntfClear.

This is one of the reasons why mixing object references and interface references can be dangerous.

The solution to the problem is to find all interface references in your code and make sure that you set them to nil before the object which implements the interface is destroyed. This can be tricky to do because there is not a good way (that I know of) to determine which reference is causing the AV.

But I bet it will solve your problem.

{ 7 } Comments

  1. Pablo Reyes | April 4, 2005 at 4:07 am | Permalink

    Please, take a look at QC 9500

  2. Craig Stuntz | April 4, 2005 at 8:12 am | Permalink

    Pablo, I closed that report as it isn’t a bug and there’s already an earlier feature request for the issue. See the report for more detailed comments.

  3. Pablo Reyes | April 9, 2005 at 9:22 am | Permalink

    I prefer to respond here because I don’t want to open QC 9500 again.

    I think the problem is the difference between "object/interface lifetime" and "object/interface reference".

    I’m not mixing "object/interface lifetime" but I do mixing "object/interface reference". I’m using descendants of TComponent implementing interfaces. I want to create and destroy an object and I want to reference an interface.

    I understand it is an error to mix "object/interface lifetime" but

    why is not possible to mix "object/interface reference"?.

    If I don’t want to use "interface lifetime" I should have a way to tell the compiler not to add calls to _AddRef and _Release.

    The compiler always implement reference count when using interfaces (not to destroy the object but to call _AddRef and _Release).

    Your solution to the problem (last paragraph) is to tell the compiler when to call _Release to prevent a call to _Release after the object was destroyed.

    I you prefer I can add this post to QC 9500.

    Thank you for your response.

  4. Craig Stuntz | April 9, 2005 at 8:05 pm | Permalink

    Pablo,

    It is possible — but tricky — to mix object and interface references in Win32 because interfaces in Win32 are designed to be reference counted. You can avoid the actual reference counting by returning -1 for _AddRef and _Release, but you can’t change the design.

    The feature request you just made in your comment (never call _AddRef or _Release at all) is already in QC in a much older report. I referenced the exact number in my comment on your report. So there is no need to add this feature request to your report because it is already in QC. Again, see my comment in QC for the number of that request in case you’d like to vote on it.

    Finally, note that this is never a problem in Delphi for .NET since garbage collection means that reference counting is never necessary in managed code.

  5. Andreas | April 11, 2005 at 10:28 am | Permalink

    So all Interface variables should be nil after destroying the object behind it.

    For global Interface variables I put some code in the finalization part of the unit and it works:

    fw: IBase;

    finalization

    if Assigned(fw) then begin

    Pointer(fw) := nil;

    end;

    end.

  6. Pablo Reyes | April 12, 2005 at 7:31 am | Permalink

    Craig,

    Thank you very much for your response.

    I had read QC 9157 before creating QC 9500 but I decided to create QC 9500 because the workaround of QC 9157 didn’t work for me. I also had already voted for QC 9157 and you are right, the feature request of both cases is the same.

    In my code sometime is impossible to assign nil to an interface reference variable because there is not such a variable. For example:

    function GetSomething: IMyInterface;

    procedure DoSomething;

    begin

    GetSomething.DoSomething;

    // For some reason destroy the object (IMyInterface implementor)

    end;

    In this case the compiler calls _Release at the end of DoSomething procedure.

    Anyway, I have found a workaround that works for me so this is not a problem for me anymore.

    PD: In Delphi .NET I have another problem. See QC 6790

  7. Pablo Reyes | April 12, 2005 at 7:38 am | Permalink

    Andreas,

    I’m doing this:

    type

    TInterfacedComponent = class(TComponent, IInterface)

    private

    FRefCount: integer;

    FDestroyObject: boolean;

    protected

    { IInterface }

    function _AddRef: Integer; stdcall;

    function _Release: Integer; stdcall;

    public

    procedure BeforeDestruction; override;

    destructor Destroy; override;

    procedure FreeInstance; override;

    end;

    implementation

    { TInterfacedComponent }

    procedure TInterfacedComponent.BeforeDestruction;

    begin

    // The object is destroyed only if there are no references to it

    if FRefCount = 0 then

    inherited;

    end;

    destructor TInterfacedComponent.Destroy;

    begin

    // The object is destroyed only if there are no references to it

    if FRefCount = 0 then

    inherited

    else

    // Flag to call free on the last call to _Release

    FDestroyObject := True;

    end;

    procedure TInterfacedComponent.FreeInstance;

    begin

    // The object is destroyed only if there are no references to it

    if FRefCount = 0 then

    inherited;

    end;

    function TInterfacedComponent._AddRef: Integer;

    begin

    // No reference count is taking place

    Result := -1;

    Inc(FRefCount);

    end;

    function TInterfacedComponent._Release: Integer;

    begin

    // No reference count is taking place

    Result := -1;

    Dec(FRefCount);

    if (FRefCount = 0) and FDestroyObject then

    Free;

    end;

    end.

    Problems I have found with this:

    - Destroy could be called more than once so I must use FreeAndNil to destroy internal objects

    I also have similar classes for TDataModule, TForm and TFrame. So all my classes implementing interfaces descends from these classes.

Post a Comment

Your email is never published nor shared. Required fields are marked *

Bad Behavior has blocked 1846 access attempts in the last 7 days.

Close