In delphi 2009 I have a reference to a IInterface
which I want to cast to the underlying TObject
Using TObject(IInterface)
obv
You are right. Beginning with Delphi 2010, you are able to use the as operator, e.g. via aObject := aInterface as TObject
or even aObject := TObject(aInterface)
.
This as
operator use a special hidden interface GUID (ObjCastGUID
) to retrieve the object instance, calling an enhanced version of TObject.GetInterface
, which does not exist prior to Delphi 2010. See source code of System.pas
unit to see how it works.
I've published some code working for Delphi 6 up to XE2, including Delphi 2009.
See http://blog.synopse.info/post/2012/06/13/Retrieve-the-object-instance-from-an-interface
There is a nice alternative when you know the implementing object is a TComponent
descendant.
You can use the IInterfaceComponentReference
interface, which is defined up in Classes
unit:
IInterfaceComponentReference = interface
['{E28B1858-EC86-4559-8FCD-6B4F824151ED}']
function GetComponent: TComponent;
end;
And then it's declared in TComponent
(and implemented to return self
):
TComponent = class(TPersistent, IInterface, IInterfaceComponentReference)
So if you know the implementing object is a TComponent
then you can do this:
function InterfaceToComponent(const AInterface: IInterface): TComponent;
var
vReference: IInterfaceComponentReference;
begin
if Supports(AInterface, IInterfaceComponentReference, vReference) then
result := vReference.GetComponent
else
result := nil;
end;
Hallvard's hack is very specific to how the Delphi compiler generates code. That has been remarkably stable in the past, but it sounds like they changed something significant in Delphi 2009. I only have 2007 installed here, and in that, Hallvard's code works fine.
Does GetImplementingObject return NIL?
If so, then if you debug and set a break-point on the case statement in the GetImplementingObject routine, what does the value of QueryInterfaceThunk.AddInstruction evaluate to in the debugger?
var
N, F: NativeInt; // NativeInt is Integer(in delphi 32bit )
S: TObject;
begin
N := NativeInt(Args[0].AsInterface) - 12;
{subtract 12 byte to get object address(in x86 ,1 interface on class) }
S := TObject(N);
writeln(S.ToString);
end;
In short: you shouldn't or add an interface with a method that returns the pointer for you. Anything else is hackery.
Note that an interface "instance" may be implemented in another language (they are COM compatible) and / or may be a stub for something out of process etc etc.
All in all: an interface instance only agrees to the interface and nothing else, certainly not being implemented as a Delphi TObject instance
Instead of relying on Delphi's internal object layout you could also have your objects implement another interface which would simply return the object. This, of course, only works if you have access to the source code of the objects to begin with, but you probably shouldn't even use these hacks if you don't have access the source code of the objects.
interface
type
IGetObject = interface
function GetObject: TObject;
end;
TSomeClass = class(TInterfacedObject, IGetObject)
public
function GetObject: TObject;
end;
implementation
function TSomeClass.GetObject: TObject;
begin
Result := Self;
end;