Mysterious case of unexpected implicit interface variable

Recently, I came across some behavior that I simply could not and cannot explain related to Delphi interface variables.

Essentially, it comes down to the implicit interface variable that the compiler generates in the Broadcast method.

At the end of the instruction that completes the method, the epilogue code contains two IntfClear . One of which I can explain, it corresponds to the local variable Listener . Another that I can’t explain, and it will take you to TComponent._Release (Debug DCU) after destroying the instance of the object. This does not lead to AV, but it is just lucky, and with full debugging FastMM reports instance access after destruction.

Here is the code:

 program UnexpectedImplicitInterfaceVariable; {$APPTYPE CONSOLE} uses SysUtils, Classes; type IListener = interface ['{6D905909-98F6-442A-974F-9BF5D381108E}'] procedure HandleMessage(Msg: Integer); end; TListener = class(TComponent, IListener) //TComponent._AddRef and TComponent_Release return -1 private procedure HandleMessage(Msg: Integer); end; { TListener } procedure TListener.HandleMessage(Msg: Integer); begin end; type TBroadcaster = class private FListeners: IInterfaceList; FListener: TListener; public constructor Create; procedure Broadcast(Msg: Integer); end; constructor TBroadcaster.Create; begin inherited; FListeners := TInterfaceList.Create; FListener := TListener.Create(nil); FListeners.Add(FListener); end; procedure TBroadcaster.Broadcast(Msg: Integer); var i: Integer; Listener: IListener; begin for i := 0 to FListeners.Count-1 do begin Listener := FListeners[i] as IListener; Listener.HandleMessage(Msg); end; Listener := nil; FListeners.Clear; FreeAndNil(FListener); end;//method epilogue: why is there a call to IntfClear and then TComponent._Release? begin with TBroadcaster.Create do begin Broadcast(42); Free; end; end. 

And here is the analysis of the epilogue:

enter image description here

There, clear as day, are two calls to IntfClear.

So who can see the obvious explanation that I am missing?




UPDATE

Well, Uwe got it right away. FListeners[i] requires a temporary implicit variable for its result variable. I have not seen this since I assigned Listener , but, of course, another variable.

The next option is an explicit representation of what the compiler generates for my source code.

 procedure TBroadcaster.Broadcast(Msg: Integer); var i: Integer; Intf: IInterface; Listener: IListener; begin for i := 0 to FListeners.Count-1 do begin Intf := FListeners[i]; Listener := Intf as IListener; Listener.HandleMessage(Msg); end; Listener := nil; FListeners.Clear; FreeAndNil(FListener); end; 

When writing this method, it is obvious that Intf cannot be cleared to epilogue.

+8
interface delphi
Mar 18 2018-11-18T00:
source share
2 answers

Just suppose, but perhaps FListeners[i] as IListener uses a temporary variable for FListeners[i] . In the end, this is the result of a function call.

+12
Mar 18 '11 at 16:17
source share

Uwe Raabe is true if you look at the code further:

 Project4.dpr.51: Listener := FListeners[i] as IListener; 00441C16 8D4DE0 lea ecx,[ebp-$20] 00441C19 8B55F4 mov edx,[ebp-$0c] 00441C1C 8B45FC mov eax,[ebp-$04] 00441C1F 8B4004 mov eax,[eax+$04] 00441C22 8B18 mov ebx,[eax] 00441C24 FF530C call dword ptr [ebx+$0c] 00441C27 8B55E0 mov edx,[ebp-$20] 00441C2A 8D45F0 lea eax,[ebp-$10] 00441C2D B9A81C4400 mov ecx,$00441ca8 00441C32 E8A573FCFF call @IntfCast 

You can see how the result of calling FListeners [i] is placed in [ebp- $ 20], and then procedure _IntfCast(var Dest: IInterface; const Source: IInterface; const IID: TGUID); (eax is the target, [ebp- $ 10], edx source, [ebp- $ 20], and ecx is the address where you can find the corresponding pointer.

You can fix your code by changing the Broadcast method to:

 procedure TBroadcaster.Broadcast(Msg: Integer); var i: Integer; Intf: IInterface; Listener: IListener; begin for i := 0 to FListeners.Count-1 do begin Intf := FListeners[i]; if Supports(Intf, IListener, Listener) then Listener.HandleMessage(Msg); end; Listener := nil; Intf := nil; FListeners.Clear; FreeAndNil(FListener); end;//method epilogue: why is there a call to IntfClear and then TComponent._Release? 
+11
Mar 18 '11 at 16:27
source share



All Articles