Friday, August 21, 2009

The “Nitty Gritty” details on Casting an Interface Reference in Delphi 2010

Malcolm Groves let loose on a new Delphi compiler feature where you can now cast an interface reference to the implementing Delphi class. Joe White was interested in the “gritty” details about how this was implemented. It turns out that it was deceptively easy once I figured out the internal compiler logic for handling “is” tests and “as” and hard casts. All three cases use the same internal mechanism implemented in the Delphi RTL.

Because all Delphi interfaces have three core methods, QueryInterface, _AddRef, and _Release, you know that you can always call QueryInterface on any interface reference. QueryInterface is declared thusly:

function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;

The last parameter is an “untyped out” parameter meaning that it will accept a reference to any variable of any type. What if you called QueryInterface with a special GUID (remember, GUID = Globally Unique IDentifier) and instead of assigning an interface reference to the Obj parameter, the implementing class instance was assigned instead? The base implementation of QueryInterface simply calls TObject.GetInterface. It is in that method that code will now look for this special GUID (which is declared in the implementation section of System.pas) and assign Obj the object reference instead of a new interface reference.

Granted, this technique does have a couple of potential issues. If, for some reason, the user decides to manually implement QueryInterface and never calls TObject.GetInterface, then none of the above casting and “is” testing will work. It will behave as if the implementing class was not implemented in Delphi. Another caveat, is if you’re doing cross-process marshaling of interfaces where both sides are Delphi 2010, you should not attempt to use this technique. In fact, if you’re doing any kinds of traditional COM programming, using this feature can yield strange results. This feature was a direct result of many requests from customers who are using interfaces for purposes outside COM.