In this section we look at COM interfaces. We start off by
looking at interfaces
in general. Then we look at the basics of COM interfaces. After
this we look at IUnknown in
some detail.
For those of you interested in a general discussion of
interfaces, go here.This
section looks at what defines an interface, what an object-oriented
interface looks like, what is so good about interface-based
programming and what makes a good interface. This section is
optional.
Any of the following could accurately describe an interface.
- A protocol of agreed-upon behavior that allows unrelated
entities to interact
- A well-defined abstraction/encapsulation layer between some
client code and some server code
- Abstract behavior in the form of a collection of logically
(semantically) related requests that a client can make of a server
component
The totally abstract nature of an interface allows us to separate
interface from implementation.
COM interfaces are a collection of logically related virtual
functions that are used in the abstract by a client and implemented
and exposed by a server. As far as the client is concerned they
represent abstract types. The server must make them concrete.
We saw in the "COM as a
better C++" chapter how to provide a binary standard interface
between a client and a server written in C++. We employed a pure
abstract base class to represent the collection of abstract
functions, then defined certain rules governing how to use it. Take
that idea a step further.
It would be really useful to define an interface mechanism that
could bridge the gap between a client and server written in
different languages and running in different environments. To define
such an interface that matches the disparate type systems of
different languages and that supports different platforms requires
the following:
- A binary standard interface mechanism that could be used from
all languages and that could span all platforms.
- A way of uniquely specifying the interface in such a way that
it makes sense from all programming languages and on all
platforms. One approach would be to define such an interface in a
description language that is independent of programming language
and platform, but has a syntax to support the interface paradigm
and incorporate a set of universally understood types, a standard
set of calling conventions, etc. This description language could
be translated to a programming-language specific syntax for any
given platform.
- A set of rules governing how the interface is used, covering
runtime type discovery, acquiring an interface, relinquishing an
interface, etc. These rules must have meaning in all programming
languages.
We end up with a COM interface.
A COM interface is a named collection of abstract methods that
define a behavioral protocol between a client and a server. Each COM
interface definition comprises a number of parts.
- The exact binary layout in memory of the interface and the way
it is accessed. The mechanism that COM uses is based on a table of
virtual functions. Each interface on a given platform adheres to
this binary layout and access mechanism.
- The tightly defined syntax of the interface. This
uniquely identifies the interface and unambiguously defines the
contents and attributes of the interface, its methods, and their
parameters. The special language that COM uses to define this is
IDL. The IDL for each interface defines a well-known stack frame
per platform that implies a well-known network representation.
- Well-formed rules about how each interface must be
manipulated. These cover dynamic type discovery of interfaces,
casting from one interface type to another, acquiring interfaces,
relinquishing interfaces, and duplicating interface references.
- The loosely defined semantics of the interface.
This is important because it gives alternate implementations in
the same problem domain the chance to provide the same interfaces.
It allows for polymorphic implementations.
As we shall see in a moment, there is only one true way to define
a COM interface and that is using Interface Definition Language
(IDL), the language of COM. However, the exact syntax used to
represent a COM interface in any given implementation language may
differ.
| Defining an interface in IDL
|
IDL was originally a language for defining DCE RPC interfaces
between client and server. We will see later that DCOM is built on
RPC. COM IDL is a superset of RPC IDL that deals with object
semantics and object reference types. IDL is very C-like. The idea
of IDL is that it allows us to define data types and interfaces in a
language neutral way. The IDL file is then compiled to create
- C/C++ language bindings (given that the syntax is so close
this makes sense)
- Language-neutral bindings that can be used from any
programming language
- Proxy/stub code for remoting the interface
We have already seen that a COM interface must be uniquely
identified and its methods must all have a well-known stack frame.
It is not possible to define this level of detail in C/C++-like
syntax. Where it is not possible to provide unambiguous definitions
using C-like syntax, attributes can be defined in square brackets to
disambiguate.
Here is an example IDL file, foo.idl.
import
"unknwn.idl"; [object,uuid(DEFACED1-0229-2552-1D11-ABBADABBAD00)] interface
IFoo : IUnknown { HRESULT
SomeMethod([in]short *ps); };
[uuid(DEFACED2-0229-2552-1D11-ABBADABBAD00)] library
FooLib {
importlib(”°stdole32.tlb”±); interface IFoo; //
cause TLB inclusion }
Notice that the line
importlib(”°stdole32.tlb”±);
is required in order to use the standard COM types without
including their definition in the type library.
If an interface is defined at global scope, MIDL will emit code
to marshal the interface so it can be used remotely. If exported
types are referred to inside a library block, MIDL will emit a type
library definition that can be used to generate language-specific
bindings.
Notice the following
- Square brackets that define the attributes of the interface.
The uuid() attribute defines the unique identifier for the
interface. The in attribute specifies that the method parameter is
an input only parameter.
- The interface is derived from IUnknown, the mother of all COM
interfaces.
- The method returns an HRESULT.
All of these points are explained later.
The file imports the file unknwn.idl. This contains the IDL
definition for IUnknown. There are a bunch of system supplied IDL
files that describe system-defined interfaces in IDL. You will need
to import these whenever you make reference to any system-defined
interfaces. Here is a list of the most commonly used IDL files.
- WTYPES.IDL - basic data types
- UNKNWN.IDL - core type interfaces
- OBJIDL.IDL - core infrastructure interfaces
- OLEIDL.IDL - OLE interfaces
- OAIDL.IDL - Automation interfaces
- OCIDL.IDL - ActiveX Control interfaces
We run this IDL through a compiler called the Microsoft IDL
(MIDL) compiler. This will spit out a number of things.
- C/C++ language specific bindings in foo_i.c, and foo.h
- Type library language independent bindings usable from any
language
- Marshalling code for remoting this interface
Here is a diagram that illustrates.

Remember back to the "COM as a better C++" chapter. We had the
problem of uniquely identifying interfaces. We used strings. It
would be quite easy to imagine a situation where two developers
independently define completely different interfaces and identify
them with the string "IFoo". When they put them in the public domain
trouble ensues.
COM interfaces are uniquely identified by 128-bit numbers called
GUIDs. These 128 bit numbers are allocated by the system using an
algorithm that is guaranteed not to provide duplicates (at least
until the year 3034!). In fact, GUIDs are language-neutral type
identifiers used to identify COM Interfaces (IIDs), COM
Classes (CLSIDs), COM Type Libraries (TLBIDs), and so on. Anything
that needs to be uniquely identified in COM is given a GUID. This guaranteed uniqueness solves namespace collisions but is
quite unfriendly to users and programmers alike. The GUIDGEN tool can be used to generate
GUIDs. It is just a wrapper around the COM API CoCreateGuid() that can be called by any COM code.
Generally you don't need to allocate GUIDs programmatically, since
they are allocated once by some tool (like GUIDGEN) at design
time.
Because C/C++ doesn't have 128-bit numbers GUIDs are represented
as a structure named GUID in C++. Likewise they are represented as a
class (com.ms.com._Guid) in Java. Look at the IID structure that
follows. This is just a typedef for the GUID structure.
An IDL file requires string-based GUIDs. The COM runtime system
also requires that certain configuration information be in the
registry, identified by a string-based GUID. When GUIDGEN generates
a new GUID it provides structure-based definitions and string-based
definitions. There are also COM APIs such as StringFromGUID2() and CLSIDFromString() that will convert.
| How are COM interfaces represented in a
particular language? |
In C++ interfaces are not defined as part of the language.
However, with some attention to care and detail we can simulate them
with a pure abstract base class. Here is some of the output from
running foo.idl through the MIDL compiler to get foo.h and foo_i.c
that describes C/C++ language mappings.
extern const IID IID_IFoo; struct
IFoo : public IUnknown { virtual HRESULT
_stdcall SomeMethod(short *ps) = 0; }; const IID IID_IFoo =
{0xDEFACED1, 0x0229, 0x2552, { 0x1D, 0x11, 0xAB, 0xBA, 0xDA,
0xBB, 0xAD, 0x00 } };
Java and VB mappings are generated from the type library,
foo.tlb.
VB has the confusingly named "non-creatable" class as its
expression of an interface.
Java has its own interfaces defined as part of the language. So
the IFoo interface would look like this in Java:
public interface IFoo
{ public void SomeMethod(short[]
ps); }
However, it would not be a COM interface but a Java interface. If
we were to use the JACTIVEX.EXE tool that comes with Visual J++ 6,
we could parse the type information in foo.tlb and get a Java
definition of a COM interface that looks like this.
/**
@com.interface(iid=com.ms.com.IUnknown, thread=AUTO) */ public
interface IFoo extends
com.ms.com.IUnknown { /**
@com.method(vtoffset=0, addFlagsVtable=4)
@com.parameters([in,size=1,type=ARRAY] ps) */
public void SomeMethod(short[] ps); public
static final com.ms.com._Guid iid = new
com.ms.com._Guid((int)0xDEFACED1, (short)0x0229,
(short)0x2552,
(byte)0x1D, (byte)0x11, (byte)0xAB,
(byte)0xBA,
(byte)0xDA, (byte)0xBB, (byte)0xAD, (byte)0x00); }
Notice the javadoc comments, /** */. They define information
meaningful to the Visual J++ 6 compiler. It will use this
information to build a .class file annotated with information that
the VM understands in order for it to deal with this COM interface.
Notice also that IFoo derives from the interface
com.ms.com.IUnknown. This is a Microsoft Java interface that defines
the behavior of IUnknown.
At the moment, you don't need to understand the details of how
IDL syntax maps to specific language syntax. You just need to
appreciate that, given the right tools, IDL can be used to generate
language mappings for a COM interface in potentially any
language/environment. How that language/environment provides the
support that allows a client to call the interface or a server to
implement the COM interface is not interesting here.
Let's take a moment to do a lab that allows us to define some
interfaces.
| HRESULTS and Error handling
|
Did you notice that our method returned an HRESULT? Let's
investigate this.
Remoting
Where is the server code that implements the interface IFoo going
to be in relation to the client? In the same process, in a different
process, perhaps, on a different machine? Well, it could be any of
these. Certainly for the last two cases, then the client and server
code are in different processes. The semantics for calling
remote (out-of-process) methods are slightly different to those for
calling local (in-process) methods. Mainly, the client and server do
not share the same stack!
This means there has to be some glue code that sits between the
client and server. It allows the client to think it is calling the
IFoo interface on an in-process object and allows the server object
to think that its IFoo interface is being called by an in-process
client. If this is to be transparent to both client and server, the
interface must be remoted in the following way.
- There has to be an object in the client's address space that
exposes IFoo and looks, feels, tastes, and smells like the real
object. When the client calls a method on IFoo all it does is
forwards a request message to the server that fully describes the
method call, waits for a response, then passes any results back to
the client. Let's call this a proxy.
- There has to be an object in the server's address space that
accepts the message and calls the real object. It harvests any
results from the real object and returns a response message
describing them. Let's call this a stub.

The remoting protocol used by the proxy and stub to exchange
messages is called ORPC, an extension of DCE RPC. The process of
serializing the data to/from the method call into messages to be
exchanged is called marshalling. The format of marshalled data is
very tightly defined and is defined by an extension to DCE NDR.
The code for the proxy and stub is generated from the MIDL
compiler, in foo_p.c and dlldata.c. So do you see how the IDL must
unambiguously define the interface, and the stack frame for every
method in it, for transmission across the network? That is what IDL
attributes are mostly about. They provide MIDL with unambiguous
information to generate marshalling code for when this interface is
used out-of-process. We will look at some IDL attributes in a
moment.
When you call a method, it can report an error by either
throwing an exception or returning an error code. Today, we have no
way in IDL of describing typed exceptions such as the ones you may
have seen in C++ or Java. However, there is an exception
throwing/catching mechanism in COM that can be used as the basis for
mapping COM exceptions to language-specific exceptions. There is
just no way of describing it in IDL. This is beyond the scope of
this discussion right now. If you would like more information, go
here.
So at the moment, when we define interface methods in IDL we are
stuck with error codes.
When we call a remote interface implementation, we have the
possibility of errors being generated by the server object or by the
remoting/marshalling layer. The remoting/marshalling layer needs a
guaranteed way of being able to do the following.
- Specify that a server-defined error has occurred.
- Specify that a remoting/marshalling error has occurred.
- Make sure the two do not get confused.
- Allow the client to be oblivious of where errors are
generated, yet aware that there has been an error.
In order to achieve this, the ORPC messaging protocol contains a
DWORD field in the response message that describes errors from the
server or from the marshalling layer. Whatever the server returns
from its interface method calls will be returned in this field. This
field has to be formatted in such a way that we can tell whether a
remoting/marshalling error occurred or a server error occurred. We
must settle on a standard error field format. This is what an
HRESULT is.
An HRESULT is just a 32-bit value that is partitioned up in such
a way as to allow the remoting/marshalling layer to overload it with
communications errors. Here is what it looks like.

The top bit determines whether this code represents success or
failure. This information can be extracted by the client using the
SUCCEEDED(hr) or FAILED(hr) macros as follows.
IFoo *pf;
.....
short s=10; HRESULT hr =
pf->SomeMethod(&s); if (SUCCEEDED(hr))
{ ... }
The next few bits are reserved.
The facility code defines the general technology area in which
the error occurred. These are system-defined and cannot be added to
by a programmer. The FACILITY_ITF facility code is used to
define interface-specific errors. FACILITY_NULL is used for generic
errors. The rest are fairly self-explanatory and are defined in the
system header file winerror.h.
The error code itself is facility-specific.
There are macros HRESULT_CODE(hr), HRESULT_FACILITY(hr) but you
will probably never use them. More likely, you will test for success
or failure, then perform a test for a specific error. It is also
possible to use the Win32 FormatMessage() API to look up a human-readable
description of the error at runtime.
The HRESULT names indicate the severity (S for success, E for
Error), the facility, and the code because they are in the following
format, FACILITY_SEVERITY_CODE. For example DISP_E_EXCEPTION
indicates a failure (E) occurred because an automation
(FACILITY_DISPATCH) method reported an exception code (EXCEPTION).
STG_S_CONVERTED indicates that a successful (S) conversion
(CONVERTED) was performed on a storage file (FACILITY_STORAGE).
FACILITY_NULL are for generic error codes and are implicit in the
name and follow the format SEVERITY_CODE. Here are some common
ones: S_OK means a successful yes. S_FALSE means a successful
no. E_FAIL means "something failed but I can't be bothered to
tell you what." E_NOTIMPL means an error has occurred because "it
did not make sense for me to implement this function" (bad interface
design) or "I couldn't be bothered to implement this function" (lazy
programmer). E_OUTOFMEMORY means an error occurred because a
resource could not be dynamically created. E_INVALIDARG means an
error occurred because "you passed a bad parameter." E_UNEXPECTED
means "What? You weren't supposed to call this method now, go read
the spec!" E_POINTER means "You passed me a pointer parameter
that I wasn't expecting to be NULL".
Interface implementations can use the MAKE_HRESULT(severity,
facility, code) to construct interface-specific errors.
Just to be clear, there is no reason that HRESULTs cannot
be mapped to language-specific exceptions - we just have no way of
describing it in IDL today. Also there is no reason that you cannot
return meaningful, typed results as well as a physical HRESULT.
As mentioned, IDL is a C-like language that allows us to define
COM interfaces and data types. It also allows us to define
attributes to disambiguate the behavior of the types we are
defining. Some of these attributes such as in and out
are important when we cannot assume a local context for a method
call. They are essential to allow the proxy/stub pair to
intelligently deal with parameters. For example, consider the
following interface:
[uuid(...),object] interface IFoo :
IUnknown { ...
HRESULT SomeOtherMethod([out]short *ps1, [in]long len,
[in,size_is(len)]short *ps2); }
It is necessary for us to specify attributes for parameters to
tell the marshalling layer how to interpret them. For instance, in
the first parameter we are passing a short by reference. But is it
being passed from the caller to the object, from the object to the
caller, or both? Using the out
attribute tells the marshalling layer that the parameter is being
passed from the object to the caller. So the first parameter does
not need to be sent in the request that represents this method call,
but it does need to come back in the response. The third parameter
is the address of an array of shorts. The in attribute tells the marshalling layer
that we are passing the array from the caller to the object in the
request and that it does not need to come back again in the
response. The size_is attribute
tells the marshalling layer that the size of the array is contained
in the second parameter, so it knows how many shorts to transmit in
the request. As you can probably see, using these attributes in the
correct way to provide the marshalling layer with enough information
is crucial.
We have already seen that COM methods that are going to be
remoted need to return HRESULTS. It turns out that when we remote a
method the request packet contains header information plus the [in]
parameters and the response packet contains header information plus
the [out] parameters and the HRESULT.
Next we
will look at the COM type system in some detail and how the mother
of all interfaces, IUnknown, is used to match disparate type
systems.

|