Interfaces and Interface Implementations
COM is a technology that allows objects to interact across process and computer boundaries as easily as within a single process. COM enables this by specifying that the only way to manipulate the data associated with an object is through an interface on the object. An interface defines a contract between an object and its clients which specifies the methods that must be associated with each interface and what the behavior of each of the methods must be in terms of input and output. That is to say an interface definition specifies the interface's member functions, called methods, their return types, the number and types of their parameters, and what they must do. There is no implementation associated with an interface.
COM uses the word interface in a sense different from that typically used in Visual C++ programming. A C++ interface refers to all of the functions that a class supports and that clients of an object can call to interact with it. A COM interface refers to a predefined group of related functions that a COM class implements, but a specific interface does not necessarily represent all the functions that the class supports.
Simple COM objects support only a single interface. More complicated COM objects, such as embeddable objects, typically support several interfaces. Clients have access to a COM object only through a pointer to one of its interfaces, which, in turn, allows the client to call any of the methods that make up that interface. These methods determine how a client can use the COM object's data.
Each interface is an immutable contract of a functional group of methods. You reference an interface at run time with a globally unique interface identifier (IID). This IID, which is a specific instance of a globally unique identifier (GUID) supported by COM, allows a client to ask an object precisely whether it supports the semantics of the interface, without unnecessary overhead and without the confusion that could arise in a system from having multiple versions of the same interface with the same name.
To summarize, it is important to understand what a COM interface is, and is not:
- A COM interface is not the same as a C++ class. The pure virtual definition carries no implementation. If you are a C++ programmer, you can define your implementation of an interface as a class, but this falls under the heading of implementation details, which COM does not specify. An instance of an object that implements an interface must be created for the interface actually to exist. Furthermore, different object classes may implement an interface differently yet be used interchangeably in binary form, as long as the behavior conforms to the interface definition.
- A COM interface is not an object. It is simply a related group of functions and is the binary standard through which clients and objects communicate. As long as it can provide pointers to interface methods, the object can be implemented in any language with any internal state representation.
- COM interfaces are strongly typed. Every interface has its own interface identifier (a GUID), which eliminates the possibility of duplication that could occur with any other naming scheme.
- COM interfaces are immutable. You cannot define a new version of an old interface and give it the same identifier. Adding or removing methods of an interface or changing semantics creates a new interface, not a new version of an old interface. Therefore, a new interface cannot conflict with an old interface. However, objects can support multiple interfaces simultaneously and can expose interfaces that are successive revisions of an interface, with different identifiers. Thus, each interface is a separate contract, and systemwide objects need not be concerned about whether the version of the interface they are calling is the one they expect. The interface ID (IID) defines the interface contract explicitly and uniquely.
Referring to an object implementing an interface means that the object uses code that implements each method of the interface and provides COM binary-compliant pointers to those functions to the COM library. COM then makes those functions available to any client who asks for a pointer to the interface, whether the client is inside or outside of the process that implements those functions.
An interface implementation is the code a programmer supplies to carry out the actions specified in an interface definition. Implementations of many of the interfaces a programmer can use in an object-based application are included in the COM libraries. However, programmers are free to ignore these implementations and write their own. An interface implementation is to be associated with an object when an instance of that object is created, and the implementation provides the services that the object offers.
When an object supports an interface, it must support all of that interface's methods in some way. Not all of the methods in an implementation need to do something—if an object does not support the function implied by a method, its implementation may be a simple return or perhaps the return of a meaningful error message — but the methods must exist.
For example, a hypothetical interface named IStack might define two methods, named Push and Pop, specifying that successive calls to the Pop method return, in reverse order, values previously passed to the Push method. This interface definition would not specify how the functions are to be implemented in code. In implementing the interface, one programmer might implement the stack as an array and implement the Push and Pop methods in such a way as to access that array, while another programmer might use a linked list and would implement the methods accordingly. Regardless of a particular implementation of the Push and Pop methods, the in-memory representation of a pointer to an IStack interface, and therefore its use by a client, is completely determined by the interface definition.
An instance of an interface implementation is actually a pointer to an array of pointers to methods—that is, a function table that refers to an implementation of all of the methods specified in the interface. Objects with multiple interfaces can provide pointers to more than one function table. Any code that has a pointer through which it can access the array can call the methods in that interface.
Speaking precisely about this multiple indirection is inconvenient, so instead, the pointer to the interface function table that another object must have to call its methods is called simply an interface pointer. You can manually create function tables in a C application or almost automatically by using Visual C++ (or other object-oriented languages that support COM).
With appropriate compiler support (which is inherent in C and C++), a client can call an interface method through its name, not its position in the array. Because an interface is a type, the compiler, given the names of methods, can check the types of parameters and return values of each interface method call. In contrast, if a client uses a position-based calling scheme, such type-checking is not available, even in C or C++.