Interfaces and reference equality: beware
In Delphi for Win32, you can’t necessarily use the “equals” operator to tell whether two interfaces both point to the same object. The following code:
var Foo: IFoo; Bar: IBar; begin Foo := MyObj as IFoo; Bar := MyObj as IBar; Assert(Foo = Bar);
will fail — even though both Foo and Bar point to the same object.
Let’s look at why that happens, and what you can do if you want a comparison that would succeed.
Memory layout of an interface reference
Note: If you don’t care about the technical details, and just want to know how to work around the problem, skip ahead to the “Workaround” section.
Let’s dig into the memory layout of an interface reference:
var Foo: IFoo; begin Foo := TFoo.Create;
An interface is a pointer to a pointer to an interface VMT. This means there’s a slot in the object’s instance data, just like any other instance field (except that it’s not visible in code), that automatically holds a VMT pointer. This field is automatically initialized during object construction. This is exactly the same way VMTs for Delphi classes are handled, except that the class’s VMT pointer is always at offset 0 within the block of memory allocated for the instance. Interface VMT pointers have to be at a nonzero offset, since offset 0 is already spoken for; they’re added at the end just like fields. (This will be familiar to those who remember Turbo Pascal objects, whose VMTs didn’t have to be at offset zero.)
And there’s the reason for the problem. Every interface you define gets a VMT all its own (even if it has the exact same methods as its parent interface, it still has its own VMT), so there has to be a slot in the instance data for the IFoo interface VMT, and another slot for the IBar interface VMT.
When you cast the object instance to IFoo, you’re really taking a pointer to the invisible “IFoo VMT pointer” field inside the object. When you cast to IBar, you’re taking a pointer to the “IBar VMT pointer” field. Those two are at different memory locations, so a reference-equality check will fail.
Bonus trivia: Because the interface pointer (which is also what’s passed as the implicit Self pointer when you call a method on the interface) isn’t at the same address as the object, the compiler has to generate a method stub that does a Self-pointer fixup, then call the actual method. The interface VMT points to this thunk method, not to the actual method body. You always wanted to know that, didn’t you?
A failed workaround: changing the variable types
So if the problem is the variables being of different types, let’s just make them both the same type:
var Foo: IInterface; Bar: IInterface; begin Foo := MyObj as IFoo; Bar := MyObj as IBar; Assert(Foo = Bar);
The Assert will still fail, even though both variables are of type IInterface! Why?
That’s because the Foo variable still points to the IFoo VMT pointer inside the instance. IFoo descends from IInterface (as all interfaces do), so an IFoo reference is assignment-compatible with an IInterface variable, and is stored as-is in that variable — still pointing to the IFoo VMT pointer.
So even though a reference may be stored in a variable of type IInterface, it does not necessarily point to the IInterface VMT pointer for that class. This becomes a particular problem when you add different interfaces to an IInterfaceList: they’re stored in a list of IInterface variables, but they’re not canonicalized to point to the IInterface VMT pointer. So, for example, TInterfaceList.IndexOf can give you the wrong answer, if you’re not careful about the types of your interface references.
The workaround
The fix is really simple: just as-cast both variables to IInterface. This normalizes them, and puts them on an even footing for comparison.
When you as-cast to an interface type, you get back a pointer to the interface VMT pointer for that exact interface — none of that weirdness with assignment-compatibility and putting an IFoo reference into an IInterface variable: when you cast to IInterface, by golly, you get an IInterface all the way. QueryInterface and SysUtils.Supports will also canonicalize to the interface you specify.
(You could even use something other than IInterface, as long as you know both instances will support it. But every interfaced object can be cast to IInterface, so using IInterface makes for a nice general solution.)
So to revisit the code at the top of the article, the correct way to compare the interfaces would be something like this:
var Foo: IFoo; Bar: IBar; begin Foo := MyObj as IFoo; Bar := MyObj as IBar; Assert(Foo as IInterface = Bar as IInterface);
Now, the assertion will pass.

May 10th, 2008 at 3:18 pm
[...] Should.ReferTo now works with interfaces, and can mix and match between objects and interfaces. It also correctly works around the interface reference-equality problem. [...]
May 11th, 2008 at 1:03 am
HI, Joe White.
And what about the next case?
SomeA=interface
['{008F39E4-9C7F-4ED8-AD56-56CD7FAD6477}']
procedure Method1;
end;
TABC=class(TInterfacedObject,SomeA)
protected
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
procedure Method1;
public
end;
procedure TForm1.Button1Click(Sender: TObject);
var a:IUnknown;
begin
a:=TABC.create;
if not (A=a as someA as IUnknown) then showmessage(‘Failed’);
end;
procedure TABC.Method1;
begin
//
end;
function TABC.QueryInterface(const IID: TGUID; out Obj): HResult;
var A:TInterfacedObject;
begin
Iunknown(Obj):=TInterfacedObject.Create;
Result:=S_OK;
end;
May 12th, 2008 at 6:39 pm
Uh-huh. If you’re writing code that returns new instances from QueryInterface, you deserve to have your tests fail.
May 12th, 2008 at 11:08 pm
You assert
>In Delphi for Win32, you can’t necessarily use the “equals” operator to tell whether two >interfaces both point to the same object.
And you offer the solution, that is to cast both interfaces to IInterface
>Assert(Foo as IInterface = Bar as IInterface);
My sample show that although both Interfaces (First IInterface and SomeA) use the same object
(you may find out this by inspecting the hidden compiler generated stub that fix EAX(in register calling convention), i.e. self param of interface Method
But casting both Interfaces to Iunknown fails because of overridden QueryInterface method of SomeA Interface, but both interfaces still use the same object.
To force your solution fail you may
1. create new instance, i.e. delegate implementation to an other object.
2. delegate implementation to inner(aggregated) object.
3. suppress returning any interfaces from QueryInterface and get exception.
Best regards Sergey Antonov.
This sample shows that if QueryInterface is override
May 19th, 2008 at 7:18 am
If the as-IInterface cast produce (potential) problems, you could move down a level and explicitly look at the thunka to compiler creates to find the implementing object, like in my hack:
http://hallvards.blogspot.com/2004/07/hack-7-interface-to-object-in-delphi.html
May 22nd, 2008 at 5:47 am
Well, the cast to IInterface may not help if you use the implements feature. In the example below the assertion will fail. This scenario is of quite common use and not somewhat constructed.
uses classes;
type
TImplementor = class(TInterfacedPersistent, IStreamPersist, IInterfaceList)
private
FList: IInterfaceList;
function GetList: IInterfaceList;
protected
property List: IInterfaceList read GetList implements IInterfaceList;
public
procedure LoadFromStream(Stream: TStream);
procedure SaveToStream(Stream: TStream);
end;
function TImplementor.GetList: IInterfaceList;
begin
if FList = nil then
FList := TInterfaceList.Create;
result := FList;
end;
procedure TImplementor.LoadFromStream(Stream: TStream);
begin
end;
procedure TImplementor.SaveToStream(Stream: TStream);
begin
end;
var
intf1: IStreamPersist;
intf2: IInterfaceList;
obj: TImplementor;
begin
obj := TImplementor.Create;
try
intf1 := obj;
intf2 := obj;
Assert((intf1 as IInterface) = (intf2 as IInterface), 'interfaces not equal');
finally
obj.Free;
end;
end;
May 22nd, 2008 at 8:44 am
Another way to subvert the comparison is to use the ‘implements’ keyword.
For example:
TBar = class(IFoo, IBar)
property SubObject:IFoo implements IFoo;
Then the comparison
(Bar as IFoo) as IUnknown = Bar as IUnknown will also fail.
However Microsoft’s COM guidelines are very specific that casting an object to a given interface, directly or indirectly, should always return the same pointer. In the above case you should override QueryInterface on the subobject to return the parent’s IUnknown.
May 22nd, 2008 at 7:39 pm
Marc is correct. If you’re really going to do the thing correctly, any interface should be able to QueryInterface to any other.
So if you’re using the “implements” keyword, the object being targeted by “implements” really should descend from TAggregatedObject instead of TInterfacedObject. From the comments in System.pas:
May 23rd, 2008 at 4:46 am
Please can you test your CSS layout with Internet Explorer.