What the heck is COM?

You’re likely vaguely aware that COM exists and it’s important. But what the heck is COM?

Well:

  • COM stands for Component-Object Model.
  • It refers to a set of libraries and APIs, as well as the programming patterns required to use them.
  • Its goal is to provide a way for two separate programs with no prior knowledge of each other, possibly written in different languages or on different machines, to exchange data.
  • COM libraries are made by Microsoft and come with the Windows OS (although the ideas behind COM are not Windows-exclusive).

Of course, you can have two programs interface in all sorts of ways — they can define ports to send and receive packets on, check a centralized server, or whatever else you can dream of. COM is just one of many solutions for inter-process communication.

That said, COM is appealing because it’s in-depth and reusable in a way that any communication protocol you hack together won’t be, it already solves a lot of low-level problems, and with the help of some Microsoft-made “wizards” such as the Microsoft Foundation Class library, programmers that don’t know much about the problem space and just want working code can get started quickly. Plus, a lot of Microsoft libraries already work with COM.

Unfortunately, because COM is so in-depth and re-usable, the terms and programming patterns dealing with COM are abstract and vague — since everything has to make sense regardless of programming language or whether you’re talking to another computer or your own. The phrase “Component-Object Model” itself is a great example of that vagueness in action.

See, COM is called “Component-Object Model” because everything in COM is patterned like this:

  • COM provides you with a bunch of Interfaces.
  • These interfaces declare virtual functions, but they don’t define them, so you can’t instantiate an instance of an Interface class.
  • Instead, these interfaces are implemented by Component Object Classes (also called coclasses), which you don’t have direct access to.

That is, COM is an object model (a way to represent the objects that maintain and affect state in your code) that only allows access to objects through defined interfaces (and these interfaces are the components that define those objects).

This object model is great for inter-process communication — since it doesn’t assume much about where an object is located or what your programming language can do to an object — but it has nothing to do with inter-process communication specifically. So the name “COM” doesn’t really gel with the reasons programmers use the Microsoft COM libraries.

Anyhow! How do you create a COM object?

  • Every interface or coclass has an associated GUID (globally unique identifier). In COM, they’re called CLSID for coclasses and IID for interfaces.
    • Example: DEFINE_GUID(IID_ISynchronizedCallBack, 0x74c26041, 0x70d1, 0x11d1, 0xb7, 0x5a, 0x0, 0xa0, 0xc9, 0x5, 0x64, 0xfe); is the GUID for the ISynchronizedCallback interface.
  • You call CoCreateInstance(...) (the ‘Co’ means ‘Component object’), pass in the GUID for the coclass and interface you want, and receive a pointer to an instantiated coclass which gives you the functions of the associated interface on that coclass. It fails if the coclass doesn’t support the requested interface.

You can also have an existing COM coclass object but you want a different interface into it. For instance, you could have a handle to a coclass through the IPersistFile interface — the COM interface for files that can be saved/loaded to disk, which offers Save() and Load() functions (and nothing else). That file also may have data that you can only get via an IDataObject interface and its GetData() function. So how do you go from your IPersistFile* to a IDataObject*? HINT: it’s not as simple as casting.

  • All COM interfaces extend from a base class called IUnknown.
  • The IUnknown base class has a function called QueryInterface
  • You can get a new interface for an existing object by calling QueryInterface(...) on your COM object pointer and passing in the ID of the interface you want
    • So, pMyPersistFile->QueryInterface(IID_IDataObject, &pMyDataObject) sets pMyDataObject to represent that same coclass, but with the IDataObject interface
    • If you pass in an interface that coclass doesn’t implement, you’ll get back a nullptr
  • So, that all looks like a lot more effort than C++ new and casting operations. Why go through all these hoops?

    Well, when you do C++ new, the memory for the class you’re new‘ing will only exist on your computer, and will only be managed by your process. When you use CoCreateInstance(...), you can generate these objects on a remote server (or on another process in your own computer) — and you don’t have to worry about the nitty-gritty details of doing so.

    Additionally, by having all these restrictions with interfaces, COM classes can be used in programs written in any programming language — you don’t have to necessarily understand C++ and virtual functions and inheritance to call Save() on your IPersistFile, and you can call Save() from C# to save a file managed by a C++ process on a computer a thousand miles away.

    And that’s what the heck COM is!

    Leave a Reply

    Your email address will not be published.