Basic COM  «Prev  Next»
Lesson 9Implementing COM objects
ObjectiveDefine a C++ implementation class using multiple inheritance.

Implementing COM objects

In previous lessons we used C++ classes to define COM interfaces, and looked at how to specify COM interfaces in IDL and the C++ class definitions created by compiling IDL files with MIDL. Our next step is to study the standard development paradigms used to develop COM objects in C++. In practice, three standard mechanisms are used. They all define one C++ class to implement a COM object. They differ in how the object's interfaces are handled.
Several conventions have emerged for C++ implementations of COM objects. These are using pointer member variables for each interface.

COM interface

To summarize, it is important to understand what a COM interface is, and is not:
  1. 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.
  2. A COM interface is not an object and is simply a related group of functions. A COM interface 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.
  3. 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.
  4. 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.

Essential COM
  1. Creating COM objects using member pointers to interfaces
    To create COM objects using member pointers to interfaces:
    1. Define a concrete class for each of the object's interfaces.
    2. Add member variables that are pointers to each class.
    3. Within your COM object's class code, manage these classes.

    For example:
    class CIMyComInterface : public IMyComInterface{
      provide implementations of all pure
        virtual methods in IMyComInterface
     
    };
    class CIYourComInterface : public IYourCominterface{
       provide implementations of all pure
         virtual methods in IYourComInterface  
    };
    class CMyComObject{
       CIMyComInterface    *m_pim;
       CIYourComInterface *m_piy;
       …
    };
    

    The member pointers to each COM interface can be instantiated when the COM object is created or on demand when one of the interfaces is requested. The main advantage of this technique is that most developers are familiar with using member pointers to other classes.
  2. Creating COM Objects with nested Classes:
    ... and usingmultiple inheritance.
    To create COM objects with nested classes:
    1. Define each COM interface, within the COM object, as a nested class.
    2. Declare a member variable for each class.

    For example:
    class CMyComObject{
      class CIMyComInterface : public IMyComInterface 
      {
        provide implementations of all pure
        virtual methods in IMyComInterface     
      }
      CIMyComInterface m_im;
    
      class CIYourComInterface : public IYourComInterface { 
        provide implementations of all pure
        virtual methods in IYourComInterface     
      }
      CIYourComInterface m_iy;
    };
    

    The advantage of this method is that the implementation of the COM class reflects the COM containment relationship, for example the COM interfaces are contained in the COM object.


Using multiple inheritance

Our main interest is in using multiple inheritance because this is what (ATL) Active Template Library uses. To use multiple inheritance, create a C++ implementation class for your COM object that inherits from the interface specifications in MyComInterface.h (the MIDL-generated include file from previous lessons):
class CMyComObject : public IMyComInterface,
  public IYourComInterface
{
   …
};

Class CMyComObject must implement all methods from IMyComInterface and IYourComInterface. Recall that each COM interface class is defined, either manually or via IDL, to inherit from (i.e. implement) IUnknown - QueryInterface, AddRef, and Release. At first glance it would seem that we need to implement the IUnknown methods twice - once for IMyComInterface and once for IYourComInterface. Doing so would cause a name conflict because we can not implement two identical methods with the same signature in a C++ class. The following code fragment demonstrates how we handle this situation.
class CMyComObject : public IMyComInterface,
  public IYourComInterface {
   HRESULT __stdcall QueryInterface
     (const IID& iid, void **ppv);
  
   ULONG __stdcall AddRef();
   ULONG __stdcall Release();

   //Methods for IMyComInterface
     - excluding IUnknown
  
   HRESULT __stdcall Fx1(CHAR *buf);
   HRESULT __stdcall Fx2();

   //Methods for IYourComInterface
     - excluding IUnknown
  
   HRESULT __stdcall Zx1(LONG ix);
};

When the Visual C++ compiler sees both base classes defining the methods with the same signature, it expects only one implementation of these methods in the derived class. The compiler actually generates two vtables within the CMyComObject class object, one for each interface. The IUnknown entries of each vtable point to the shared methods. We do not need to specify each function as virtual because the base classes do this.

.NET COM Complete Interoperability

CLSID or Class IDentifier

In the context of Microsoft's Component Object Model (COM), the term CLSID stands for Class IDentifier. A CLSID is a globally unique identifier (GUID) that uniquely represents a COM class object. It is essentially a unique code used to identify a specific implementation of a COM class.
Key Points about CLSID:
  • Uniqueness: Each COM class has its own unique CLSID, which distinguishes it from all other classes, not only on the local machine but across networks.
  • Registry: CLSIDs are typically registered in the Windows Registry under the `HKEY_CLASSES_ROOT\CLSID` path, where information about the corresponding COM class is stored, including the path to its code and data on how to instantiate it.
  • Instantiation: When a client application needs to create an instance of a COM object, it refers to the object's CLSID. The system uses this CLSID to look up the class in the registry, find out where the class's executable code is stored, and load it into memory to create the object.
  • In-Process vs. Out-of-Process: The registry entry for a CLSID also specifies whether the COM object should run in the same process as the client (in-process) or in a separate process (out-of-process). This is important for managing how resources are used and how communication across different components is handled.

CLSIDs are central to COM's ability to ensure that software components are reusable and interoperable, allowing different applications and services to share functionality in a modular fashion.

Like COM interfaces, COM objects are identified by a 128-bit ID or UUID. When used to identify a COM object, the IDs are called CLSIDs. The DEFINE_GUID macro can be used to define a variable representing a CLSID.
Clients and objects have different views of a COM object and its interfaces. The object sees the interface implementation via its internal C++ class. The client views the object through its interfaces, that is, its vtables.

Client and Server COM Views

As developers of both the client and the server, we know that the interface pointers used by the client in the lesson's example are part of an instance of C++ class CMyComObject. However, the client does not actually see this. Instead, it sees two COM interface pointers,
  1. one to IMyComInterface and
  2. one to IYourComInterface, and not a pointer to a specific C++ class.

COM Server and Client
The COM implementer is the server, which is mostly created with C++ and uses the ATL.
COM consumer client
  1. Can use all kinds of languages such as C#, C++, and VB.
  2. Internet controls such as the ActiveX control for client and COM on the server

What Is ATL Server?

With Windows 2000 and Internet Information Services (IIS), Microsoft introduced a platform capable of handling the mission-critical tasks described previously. Combine IIS with (ISAPI) Internet Server Application Programming Interface development and developers are capable of getting the flexibility, control, performance, reliability, and scalability they desire. Unfortunately, ISAPI development is difficult, and there is a lot of infrastructure that the developers themselves must provide before they can even think of working on their application logic. With Visual C++ .NET, the Visual C++ libraries team has created a library that greatly aids ISAPI development. That library is ATL Server.
The ATL Server library has been designed to be a lightweight, thin wrapper over ISAPI. As such, the library allows developers to maintain complete control over their Web applications (by plugging directly into the underlying layers wherever they deem it necessary) while also greatly reducing development time by giving developers much of the code that they would normally have to write, test, and document themselves.
The ATL Server classes are well-contained, separate parts that come together to form a complete “framework” that helps developers create ISAPI applications much more productively. The classes are built on top of one another, with high-level functionality built on top of low-level functionality, so nothing is hidden and the pieces can be easily replaced and changed without affecting other portions of the application.
Analyze the following uploaded image and 1) print off the text and 2) describe any relevant features.
1) Server's view of a COM Interfaces 2) Client's view of a COM object and interfaces The diagram depicts both the server's and client's views of Component Object Model (COM) interfaces and objects.
  • Server's view of a COM Interfaces
    • pointer to CMyComObject
    • IMyComInterface vtable pointer
    • IMyComInterface vtable
    • pointers to methods
    • IYourComInterface vtable pointer
    • IYourComInterface vtable
    • pointers to methods
    • internal memory layout of C++ class instance
  • Client's view of a COM object and interfaces:
    • pointer to IMyComInterface
    • IMyComInterface vtable pointer
    • IMyComInterface vtable
    • pointers to methods
    • pointer to IYourComInterface
    • IYourComInterface vtable pointer
    • IYourComInterface vtable
    • pointers to methods

Relevant Features
  • Server's View:
    • CMyComObject: This likely represents the actual COM class instance in the server's memory.
    • Interface VTables: Each interface (IMyComInterface, IYourComInterface) has a separate vtable. The vtable contains pointers to the methods that the interface supports. This allows for polymorphism in COM, where different implementations of an interface can be swapped seamlessly.
    • Internal Memory Layout: Indicates how the object and its interfaces are arranged in memory from the perspective of the C++ class implementation.
  • Client's View:
    • Interface Pointers: From the client's perspective, access to the COM object is through pointers to interfaces (IMyComInterface and IYourComInterface).
    • Method Access via VTables: Clients invoke methods on the COM object through the vtable pointers, ensuring that the client can use the object's functionalities as defined by the interfaces without needing to know the details of the implementation.

The diagram effectively illustrates the encapsulation and abstraction principles in COM, showing how COM separates the implementation (server's view) from the interface (client's view). This separation is key to achieving binary compatibility and reusability across different COM components.
1) Server's view of a COM Interfaces 2) Client's view of a COM object and interfaces

Implement Com 0bject - Exercise

Click the Exercise link below to apply what you have learned about implementation of a COM object using multiple inheritance.
Implement Com 0bject - Exercise

SEMrush Software Target 9SEMrush Software Banner 9