Re: Interface Question
- From: Rob Kennedy <me3@xxxxxxxxxxx>
- Date: Mon, 09 Oct 2006 22:58:05 -0500
alanglloyd@xxxxxxx wrote:
Inheritance for IShellLink, IPersist, and IPersistFile is declared as
...
IShellLink = interfac(IUnknown) etc etc
IPersist = interface(IUnknown) etc etc
IPersistFile = interface(IPersist) etc etc
and I then see code of ...
var
ObjectIF : IUnknown;
ShellLinkIF : IShellLink;
PersistFileIF : IPersistFile;
begin
ObjectIf := CreateComObject(CLSID_Shellink);
ShellLinkIF := ObjectIF as IShellLink;
// do something with ShellLinkIF
PersistFileIF := ObjectIF as IPersistFile;
// do something with PersistFileIF
Now IShellLink has _none_ of the methods of IPersist or IPersistFile,
so _how_ can an IShellLink be typecast to an IPersistFile. Or is the
COM object of ObjectIF somehow re-created as an IPersistFile when
type-cast as such.
Astute question! The answer is that you're not really type-casting.
I'll begin by referring you to a couple of articles on my Web site, which describe the layout of objects in memory when they implement interfaces.
http://www.cs.wisc.edu/~rkennedy/interface-object
When you call CreateComObject, you're given a pointer to a pointer to a list of functions. That list has at least three function pointers in it, corresponding to the functions defined in IUnknown. (There might be more in the list, but since you're only told you have an IUnknown, you have to assume that's all you have.)
Among those functions is QueryInterface. The "as" cast in Delphi actually ends up calling that function. This line:
ShellLinkIF := ObjectIF as IShellLink;
results in something more like this:
if Assigned(ObjectIF) then
ObjectIF.QueryInterface(IShellLink, ShellLinkIF)
else
ShellLink := nil;
That uses the function pointer from the list to invoke a function, very similar to how virtual methods get invoked in Delphi. (See the "vmt" article on my site for more how how those work.) That function receives as one of its parameters the interface value it was called on. The call above really looks more like this:
ObjectIF.QueryInterface(ObjectIF, IShellLink, ShellLinkIF);
The QueryInterface function at the other end of that pointer knows how to take that ObjectIF pointer and get back to the real object. (Listing 7 in the article above demonstrates how Delphi programs do it. I imagine Microsoft does it similarly, but COM doesn't _require_ any particular implementation technique.)
The QueryInterface function is supposed to fill its last parameter with a pointer to a pointer to a list of functions that correspond to the given GUID. Delphi objects store all those pointers as hidden fields (as shown in Listing 3), and they know which field corresponds to which GUID by looking at their class's interface table. TObject has built-in methods for reading that table.
When QueryInterface returns, the new pointer leads to a list of function pointers. Since all interfaces descend from IUnknown, they all begin with the same three functions. That's important. Without that requirement, there might be some interfaces from which you could never return. But now, all interfaces include pointers to QueryInterface functions.
Note that the QueryInterface pointer from the IUnknown table isn't necessarily pointing to the same function as the pointer in the IShellLink table. The IUnknown one probably has a different offset than the IShellLink table. (Listing 7 again.)
--
Rob
.
- Follow-Ups:
- Re: Interface Question
- From: Marco van de Voort
- Re: Interface Question
- From: Hans-Peter Diettrich
- Re: Interface Question
- References:
- Interface Question
- From: alanglloyd@xxxxxxx
- Interface Question
- Prev by Date: Re: Disabling an application during a file save?
- Next by Date: Re: Interface Question
- Previous by thread: Re: Interface Question
- Next by thread: Re: Interface Question
- Index(es):
Relevant Pages
|