Saturday, April 29, 2006

Getting a list of implemented interfaces

Frenk asked in the non-tech newsgroup:

"Is there some way to find out which interfaces (interface list) a particular component implements (I don't know [the interfaces], so querying is not possible)?"

Yes, there is.

Call TObject.GetInterfaceTable to get a pointer to the list of interfaces a specific class implements - see System.pas for details. Note that this does not include interfaces that you implement by overriding QueryInterface manually - but that is not usual for Delphi code.

For example, this code demonstrates how to dump all implementerd interfaces of a class:

program TestIntfTable;

{$APPTYPE CONSOLE}

uses
Classes,
SysUtils,
TypInfo,
ComObj;

procedure DumpInterfaces(AClass: TClass);
var
i : integer;
InterfaceTable: PInterfaceTable;
InterfaceEntry: PInterfaceEntry;
begin
while Assigned(AClass) do
begin
InterfaceTable := AClass.GetInterfaceTable;
if Assigned(InterfaceTable) then
begin
writeln('Implemented interfaces in ', AClass.ClassName);
for i := 0 to InterfaceTable.EntryCount-1 do
begin
InterfaceEntry := @InterfaceTable.Entries[i];
writeln(Format('%d. GUID = %s',
[i, GUIDToString(InterfaceEntry.IID)]));
end;
end;
AClass := AClass.ClassParent;
end;
writeln;
end;

begin
DumpInterfaces(TComponent);
DumpInterfaces(TComObject);
DumpInterfaces(TComObjectFactory);
readln;
end.
Output:
Implemented interfaces in TComponent
0. GUID = {E28B1858-EC86-4559-8FCD-6B4F824151ED}
1. GUID = {00000000-0000-0000-C000-000000000046}

Implemented interfaces in TComObject
0. GUID = {DF0B3D60-548F-101B-8E65-08002B2BD119}
1. GUID = {00000000-0000-0000-C000-000000000046}

Implemented interfaces in TComObjectFactory
0. GUID = {B196B28F-BAB4-101A-B69C-00AA00341D07}
1. GUID = {00000001-0000-0000-C000-000000000046}
2. GUID = {00000000-0000-0000-C000-000000000046}

4 comments:

Huseyn G. said...

hi Halvard,
thanks for an excellent blog. I've already found here many questions to interface related questions. But one question left, which nobody could answer so far.
In delphi we expect all interfaces be descendants of Iunknown, but in c world there are even more basic many interfaces which are not inherited from iunknown. I came across of the one in a dll which i need to use. But I couldn't do it using delphi.

I would very appreciate any answer on this very grey aspect of delphi interfaces.

Chears.

Hallvard Vassbotn said...

Hi Huseyn,

I written an answer here:

http://hallvards.blogspot.com/2006/04/pure-interfaces-in-delphi.html

omavideniz said...

I am studying active server objects. i have created an object that
has a read-only property of type variant * (its function returns
olevariant.). i use this property to return a com object to ASP, and it
will be unassigned when the object created so i assign Delphi
-Unassigned- value to it. However, the following ASP code raises
"Object required: '[undefined]'" error for the unassigned value.
Set obj = MyAspObj.theProp;

I tried VarClear or VarAsType(theProp, varDispatch) but none of them
work. Then i have tried


theProp := OleVariant(IUnknown(Unassigned));


which works fine.


Unassigned works fine clearing an object or testing a VBScript Nothing
but doesn't work to return something unassigned. i don't know this is
the right way to return a Nothing to the VBScript, or broking my leg,
or are there more proper ways to do this?

Hallvard Vassbotn said...

Hi Omavideniz,

I don't have any direct experience with your sceniario (at least not in the last 4-5 years), but I did some testing:

program TestInterfaceVariants;

{$APPTYPE CONSOLE}

uses Variants, SysUtils;

procedure Dump(const V: Variant);
begin
with TVarData(V) do
writeln(Format('%.4x:%.8x:%.8x:%.8x', [RawData[0], RawData[1], RawData[2], RawData[3]]));
end;

const
NilInterface: IUnknown = nil;
NilDispatch : IDispatch = nil;
var
V: Variant;
begin
V := Unassigned;
Dump(V);

V := OleVariant(IUnknown(Unassigned));
Dump(V);

V := NilInterface;
Dump(V);

V := NilDispatch;
Dump(V);

readln;
end.

Here I'm testing out four different ways of initializing a variant and checking out what bit patterrns they produce in the raw storage of the 16-byte variant. When you run the code, the output is:

0000:00000000:00000000:00000000
000D:00000000:00000000:00000000
000D:00000000:00000000:00000000
0009:00000000:00000000:00000000

The first value is the value of the VType: TVarType; field - the values above mean:

varEmpty = $0000; { vt_empty 0 }
varDispatch = $0009; { vt_dispatch 9 }
varUnknown = $000D; { vt_unknown 13 }

So it looks like you cannot use a varEmpty variant (=Unassigned) in the VBscript - at least not if you want to treat it like an object/interface.

Your special cast turns the variant into a varUnknown with a nil value for the interface reference. A cleaner-looking solution would probably be to use a IUnknown constant or variable that is nil, like I do in the code above:

const
NilInterface: IUnknown = nil;
...
V := NilInterface;

You might think that this version might work:

V := IUnknown(nil);

But the compiler complains with a

[Error] TestInterfaceVariants.dpr(32): Invalid typecast

error.

V := IUnknown(nil);



Copyright © 2004-2007 by Hallvard Vassbotn