Interfaces

General rules

All interfaces are derived from IUnknown. Given one COM object, the client can call the QueryInterface function of any interface to obtain any of the object’s interfaces.

An interface never has a version number: once defined, it can never be changed. If the COM object is updated, it can provide a new interface – but must retain the old interface for compatibility with old client code.

Interfaces are generally derived from IUnknown, only. In other words, an interface provides as small a set of functions as possible. A client uses lots of interfaces, rather than one interface which does everything. The reason for this policy is that if an interface is updated (e.g. from IName to IName2), the client can be modified to take advantage of the new interface – but there aren’t any derived interfaces which also have to be updated to allow the client to use the new features.

Implementation of interfaces

By convention, the C++ constant IID_IName contains the IID for the interface called IName.

An interface can be defined like a C++ abstract base class (i.e. a class with nothing but pure virtual functions). Note that an interface contains no code: it is purely an interface to the COM object which contains the implementation of these functions. For example (omitting some modifiers for clarity):

interface IUnknown
{
public:
virtual HRESULT QueryInterface(const IID* refiid, void** ppvObject) = 0;
virtual ULONG AddRef(void) = 0;
virtual ULONG Release(void) = 0;
};

This is implemented as a table of function pointers, and an interface structure which starts with a pointer to the function table – see the following example, which uses C notation (again omitting some modifiers for clarity):

struct IUnknownVtbl
{
HRESULT (*QueryInterface)(IUnknown* This, const IID* refiid, void** ppvObject);
ULONG (*AddRef )(IUnknown* This);
ULONG (*Release )(IUnknown* This);
};

interface IUnknown
{
const struct IUnknownVtbl* lpVtbl;
};

Another example, showing the physical layout in memory:

0x00010000   pOne = 0x00020000   Client's IOne* for object A  
       
0x00020000   IOne = 0x00030000   Pointer to VTable  Instance A of class CExample
0x00020004   ITwo = 0x00040000   Pointer to VTable
0x00020008   m_iValue = 13   Member variables
       
0x00030000   &QueryInterface   Pointer to method  VTable of methods for IOne
0x00030004   &AddRef   Pointer to method
0x00030008   &Release   Pointer to method
0x0003000C   &CustomMethod   Pointer to method

Note:

All interfaces derive from IUnknown, so the first three methods in the interface’s VTable are QueryInterface, etc.

An IUnknown* pointer will point to one of the class’s interfaces, e.g. IOne (pUnknown = 0x00020000).

Every instance of CExample will have the same VTable pointers (0x00030000 and 0x00040000); only one copy of the IOne VTable is required for all instances of this class.

The interface is normally defined in a Interface Definition Language (IDL) script. This can then be compiled by the Microsoft IDL compiler (MIDL.EXE, part of the Win32 SDK), which produces a C++ header file containing the interface definition, which can be used by both the client code and the object implementation code. It can also produce source code for a proxy and stub that can marshal the interface across a process boundary.

Common interfaces

IUnknown: Provided by all COM objects. All other interfaces are derived from this interface. Provides the functions:

QueryInterface: returns other interface pointers, when passed their IID, and increments the object’s reference count.

Release: tells the COM object that this interface is no longer required, and decrements the object’s reference count. When the reference count falls to zero, the object is freed from memory – and hence can no longer be accessed.

AddRef: tells the COM object that an additional reference is being made to this interface, and increments the object’s reference count.

IDispatch: Provided by "programmable", or automation, objects. Provides an Invoke function, which gives access to the object’s Methods (function calls, which tell the object to do something) and Properties (internal variables, so you can Get or Set their value). The Method or Property being called is identified via a DISPID number (unique within this object only), which can be obtained from the name of the Method or Property using the GetIDsOfNames function.

IEnumXXX: A family of enumerator interfaces, for returning a list of items of type XXX. Each provide the functions:

Reset: resets the list index to zero.

Next: returns a pointer to the next n items.

Skip: skips past the next n items.

Clone: returns another IEnumXXX interface with an independent list index.

IMalloc: Interface returned by the CoGetMalloc function. Used to allocate and free memory passed as interface function arguments.

IOleObject: Provides a DoVerb function, which allows Verbs to be issued the object – a verb is a function (without any arguments nor return value), which is typically commanded by selecting an option from an object-specific menu list. The Verb is identified by a number. One Verb that most OLE objects support is the OLEIVERB_PROPERTIES verb, which commands the object to display a Properties dialogue box.