Basic COM  «Prev  Next»
Lesson 6 COM interface properties
Objective List the binary layout requirements of a COM interface.

COM Interface Properties

The "binary layout requirements" for a Microsoft Component Object Model (COM) interface refer to the specific way in which a COM interface must be structured in memory to ensure interoperability between different components and programming languages. These requirements are critical because COM is designed to enable communication between objects in a language-agnostic and binary-compatible manner. Below are the key binary layout requirements for a COM interface:
  1. Inheritance from IUnknown:
    • Base Interface: Every COM interface must inherit, directly or indirectly, from the IUnknown interface.
    • First Three Methods: The first three entries in the virtual function table (vtable) must be the methods of IUnknown in the following order:
      1. QueryInterface
      2. AddRef
      3. Release
    • Virtual Function Table (vtable) Layout:
    • Sequential Ordering: Methods in the vtable must appear in the order they are declared in the interface definition.
    • Inheritance Hierarchy: If the interface inherits from other interfaces (other than IUnknown), the methods from the base interfaces must appear first, maintaining their original order.
    • Single Inheritance: COM interfaces typically use single inheritance to avoid complications with multiple inheritance in the vtable layout.
  2. Calling Convention:
    • Standard Calling Convention: All methods must use the __stdcall calling convention (on 32-bit systems). This ensures that the callee cleans the stack, which is important for language interoperability.
    • Consistent Across Platforms: On 64-bit Windows systems, the default calling convention is used, which is consistent across all functions.
  3. Pure Virtual Methods:
    • Abstract Methods: All methods in the COM interface must be pure virtual functions. This is represented in C++ by assigning = 0 at the end of the method declaration.
    • No Instance Data: The interface should not contain any member variables. This ensures that the object's memory layout consists only of the vtable pointer.
  4. Binary Compatibility:
    • Compiler Compatibility: The binary layout must be compatible with Microsoft's compiler conventions for virtual function tables to ensure that components compiled with different compilers can interoperate.
    • No Name Mangling: Method names should not be mangled (a process compilers use to encode additional information in function names), which is avoided by using extern "C" linkage or by adhering to COM's standard naming.
  5. GUID Association:
    • Unique Identifier: Each COM interface must have a globally unique identifier (GUID), known as an Interface Identifier (IID).
    • Consistent Identification: This GUID is used at runtime to identify and query interfaces through QueryInterface.
  6. Reference Counting:
    • Lifetime Management: The AddRef and Release methods are used for reference counting to manage the lifetime of COM objects.
    • Consistent Behavior: All COM objects must implement these methods consistently to ensure proper memory management across components.
  7. Threading Model Compliance:
    • Apartment Model: The COM interface must comply with the threading model specified for the COM object (e.g., Single-Threaded Apartment, Multi-Threaded Apartment).
    • Synchronization: Any necessary synchronization must be handled within the methods to ensure thread safety.

Example Layout in C++
Here's how a typical COM interface would be declared in C++:
struct __declspec(uuid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"))
IMyInterface : public IUnknown
{
    virtual HRESULT __stdcall MyMethod1(/* parameters */) = 0;
    virtual HRESULT __stdcall MyMethod2(/* parameters */) = 0;
    // Additional methods...
};

Memory Representation
  • Object Memory: The COM object's memory consists of a pointer to the vtable.
  • vtable Memory: The vtable is an array of pointers to the methods, starting with IUnknown methods.

Importance of the Binary Layout
  • Language Interoperability: Ensures that COM objects can be used across different programming languages that may have different class and object models.
  • Binary Compatibility: Allows COM components to interact at the binary level without source code compatibility, enabling components to be updated independently.

Additional Considerations
  • Error Handling: Methods should return HRESULT to indicate success or failure, following COM's standard error handling mechanism.
  • Marshalling Requirements: If the interface is to be used across process or network boundaries, it must adhere to COM's marshalling requirements.

By adhering to these binary layout requirements, developers ensure that their COM interfaces are compatible with the COM infrastructure, enabling seamless interaction between components, regardless of the languages or tools used to create them.

vtable or virtual table

A COM interface contains a group of related COM methods. COM interfaces have the following properties. The runtime binary layout of a COM interface contains vtable. A vtable is a table of pointers to interface methods. A COM interface pointer is a pointer to a vtable.
Methods in a vtable are accessed by table position, not by method name. The placement of functions in a vtable is called vtable order.
Characteristics of a vtable
Characteristics of a vtable A diagram depicting the structure of a Component Object Model (COM) interface in programming. Here's a breakdown of its features:
  1. COM Interface Pointer: This is the main pointer used to interact with the COM object.
  2. VTable Pointer: Points to the VTable, which contains pointers to various functions.
  3. VTable:
    • QueryInterface Pointer: Points to the `QueryInterface` function, which retrieves pointers to the supported interfaces.
    • AddRef Pointer: Points to the `AddRef` function, which increments the reference count.
    • Release Pointer: Points to the `Release` function, which decrements the reference count and potentially deletes the object if the count reaches zero.
    • Method01 Pointer, Method02 Pointer, etc.: These point to additional methods specific to the interface.

Each entry in the VTable corresponds to a function that can be called by the COM interface. The VTable is crucial for enabling polymorphism in COM objects, allowing them to expose multiple interfaces.
  • VTable Pointer
  • COM Interface Pointer
  • QueryInterface Pointer: HRESULT QueryInterface(...)
  • AddRef Pointer: ULONG AddRef(...)
  • Release Pointer: ULONG Release(...)
  • Method01 Pointer: HRESULT Method01(...)
  • Method02 Pointer: HRESULT Method02(...)
  • ... (indicating additional methods can be listed similarly)
This structure is typical in COM programming, where interface-based programming is used to ensure binary compatibility between different software components.

IUnknown COM Interface

IUnknown is an interface defined by Microsoft for use in the Component Object Model (COM). It is the fundamental base interface from which all other COM interfaces must derive. `IUnknown` provides the means for managing the lifetimes of COM objects and for allowing clients of those objects to access other interfaces implemented by those objects. It includes three essential methods:
  1. QueryInterface:Allows clients to obtain pointers to other interfaces on an object. This method is used for interface navigation and to ensure type safety,
  2. AddRef:Increments the reference count of an object. This method helps in managing the lifetime of the object by keeping track of how many clients are using it. , and
  3. Release:Decrements the reference count of an object. When the reference count reaches zero, the object is destroyed..

Collectively, these three methods are called IUnknown. The first three pointers in every vtable associated with a COM interface must point to the IUnknown methods (i.e. QueryInterface, AddRef, and Release) in order.
These methods provide the necessary mechanisms for reference counting and interface querying that are central to COM's architecture for object management and interface-based programming. Besides providing application-specific functionality, a COM interface must also provide methods to support interface navigation and lifetime management.
  • Inheritance: COM does not support implementation inheritance. Unlike C++, in COM there is no way to inherit member functions[1] and member variables from a base class. However, the standard convention used in COM is to say, "All COM interfaces inherit from IUnknown." What we really mean is that all COM interfaces implement the methods of IUnknown in vtable order, i.e., QueryInterface, AddRef, and Release are the first three functions in every COM interface.

C++ and virtual member functions

C++ is the language of choice for developing COM applications. One of the reasons for this is that C++ compilers place virtual member functions of a class into a vtable.
  • Defining COM interface: The following code declares a C++ class that, when instantiated, implements a COM interface called IMyComInterface
    class CIMyComInterface {
    
     //For C++ we usually add
     //a 'C' in front of the
     //interface name
    
        virtual HRESULT __stdcall QueryInterface
     (const IID& iid, void **ppv);
     
        virtual unsigned long __stdcall AddRef();
        virtual unsigned long __stdcall Release();
    
        virtual HRESULT __stdcall Fx1(CHAR *buf);
        virtual HRESULT __stdcall Fx2();
    };
    

    When a pointer to this class is passed to a COM client, the client can't see any part of the object other than the vtable. Data and nonvirtual member functions are not visible or accessible. Recall from the previous lesson that the first parameter to a COM interface method must be a pointer back to the interface that contains it. C++ provides automatic support for this requirement by passing in the this pointer as an implicit first parameter.
  • this pointer as a COM interface pointer:
    The this pointer of a C++ class instance points to the internal class object built by the C++ compiler. A C++ class with virtual functions contains a vtable to access those functions. Because the vtable is the first object in the C++ class object, the this pointer is in effect a pointer to a pointer to a vtable. That is, it has the same binary layout as a COM interface.
    Why we have to use the corresponding interface pointer as the this parameter.
    There are of course other means to do that, as long as you are the implementer of the component. But this is not the case for the clients of our component. When the component is built using the COM way, clients of our component know nothing about the internals of our component. Clients can only take hold of the interface pointer, and this is the very pointer that will be passed into the interface method as the this parameter. Under this expectation, the compiler has no choice but to generate the interface method's code based on this specific this pointer.
    So the above reasoning leads to the result that: It must be assured that each function in a vtable must recieve the corresponding interface pointer as its "this" parameter.

Essential COM

Generating IIDs

A predefined macro called DEFINE_GUID is used to define IIDs as variables in your code. Microsoft provides a tool called guidgen to generate IIDs and DEFINE_GUID macros.
The guidgen tool
COM guidgen tool

Guidgen will generate a DEFINE_GUI macro for you. Click the New GUID button to generate a new GUID. Click the Copy button to copy the DEFINE_GUID macro with the generated IID copied into the clipboard. Paste the DEFINE_GUID macro into your code. Replace <<name>> with the interface name. For example:
DEFINE_GUID(<<name>>, 
0xc4cf2171, 0x4832, 0x11d2, 0x85, 0xbc, 0x8,
0x0, 0x17, 0x0, 0xc5, 0x7f);

Replace <<name>> with the name of your interface:
DEFINE_GUID(IMyComInterface, 
0xc4cf2171, 0x4832, 0x11d2, 0x85, 0xbc, 0x8,
0x0, 0x17, 0x0, 0xc5, 0x7f);

An IID (interface ID) is a 128-bit number. IIDs are named after the interface they identify. For example, IUnknown has a predefined IID called IID_IUnknown. IIDs are also called GUIDs or UUIDs.

Defining Com Interface - Exercise

Click the Exercise link below to apply what you have learned about COM interface requirements.
Defining Com Interface - Exercise
[1] member functions:A C++ member function is a function that is declared within a class definition. It operates on objects of that class and has access to all the data members (variables) and other member functions within that class.

SEMrush Software