COM

COM - It’s Not Dead, It’s Undead

Most software falls into one of three categories - alive (like .NET Core), done (like .NET Framework), or dead (like Internet Explorer). COM is all three at once. ActiveX is dead, Drag ’n Drop is done, and new interfaces pop up occasionally, like IInspectable. There are a lot of COM zombies running on your system right now.

Note: WinRT is based on COM, and avoids many of the problems of ‘classic’ COM.

Features?

COM is many things.

  1. By it’s nature, COM tends to expose difficult problems, and forces you to deal with them head-on. This is the benefit of interfaces.
  2. But, COM also introduces its own set of problems, lots of details, and tuning parameters.
  3. In a rush to market, the COM API is riddled with inconsistent and confusing naming.
    • On top of this is a whirlwind of ever-changing, ’90s-era marketing buzzwords.

None of this stuff fits in your head. I can tell you what OLE Automation is, but I can’t recall all the interfaces in an OLE custom control, or the definition of OLE itself.

COM Is Hard

A COM server has more moving parts than a helicopter. Suppose you had a problem, and you solved it by writing a COM server. Now you have two problems.

  • Implementing an ActiveX control is often more complex than the business problem it is intended to solve. Win32/COM development is really system programming.
  • You can’t write it by hand. Technically, you can, and that was the only option until ATL came out. AFAIK, nobody outside of Microsoft actually got it right.
  • It requires low-level understanding of COM’s inner workings, as well as broad knowledge of the interfaces. In order to troubleshoot any of it, you have to understand all of it.
  • DLL hell. This problem is not unique to COM, or even unmanaged code, but COM tends to accentuate it.
    • For a long time there was a misconception that interface-based programming would, in itself, solve all our compatibility issues. It doesn’t. An implementation can change quite a lot and still support the same interface. Two subtle causes of breaking changes are changing the order of operations and changing default settings.
    • Installing a custom control makes it global to the system, whether you want it to be or not. Registering a bunch of COM objects on your system written by different vendors will slow down the boot time and make it unstable. (WinRT and DirectX take a different approach.)
    • COM amplifies this problem through aggregation. When you load in a COM object, it may in turn load in more components behind the scenes, increasing the odds of this happening.
  • Good luck debugging.
  • Just like the early internet protocols, COM was designed to be used in a trusted environment. Security and licensing are not part of the core design.
  • DCOM will not play nicely with your firewall or security team.

When originally released and throughout the ’90s, the idea was that ISVs would publish libraries of COM components, and the task of software development would evolve into mainly connecting them all together, like Legos. This didn’t pan out. Software is just plain complicated, and adding COM to your project does not make it any simpler.

It turns out that in order to use COM components, you have to completely trust their publisher, they have to be very well tested, and they must not break any other controls installed on the target system. Ideally, you would also have access to their source code.

That being said…

COM does have its upsides though, and in certain environments, these requirements can be met. One such place is at Microsoft itself. If you treat COM objects as extensions to the OS, it can be thought of as a systems programming tool.

Also, creating a COM client is pretty simple. Ever since .NET came into being, the guidance has been something like, leave the COM server development to MS, but feel free to use it as a client.

  • Windows has a number of COM Support Features and removing them would break everything.
  • Communications with InProc servers is fast.
  • .NET Interop supports access as a client. Many common tools are automation clients, e.g. PowerShell can load in Windows Script Host.
  • Components written by Microsoft can mostly be trusted.
  • ATL helps you sidestep a lot of traps.
  • Learning COM is a big part of understanding Windows.

WinRT provides a large collection of system-provided COM objects. It’s a clean, well thought out API. Again, COM is easy when you’re the client. There are WinRT libraries for many popular languages, including C++, C#, and Rust.

COM will continue to haunt us for the foreseeable future - and possibly eternity.

The Basics

To implement a COM class, you need to

  • Write the the class, implementing IUnknown correctly.
  • Write a class object which implements IClassFactory, so it can be instantiated.
  • Add IDispatch (or a dual interface) if the object is to be available to a scripting environment.
  • Add a reference counting mechanism to the server.
  • Add self-registration to the server:
    • Add the correct entry points if the server is a DLL: DllGetClassObject, DllCanUnloadNow, DllRegisterServer, and DllUnregisterServer.
    • Add calls to CoRegisterClassObject if the server is an EXE.

Because this is a lot of boilerplate and is also error prone, you’ll want to use ATL. Let the AppWizard do the heavy lifting for you.

Subtopics

Types

A large number of related types are part of OLE automation , here are a few of the more common ones.

Most COM objects implement multiple interfaces statically, through multiple inheritance. COM also supports dynamic composition.

Interfaces and user-defined types can be defined in Type Information.

COM+

There are (or were) several types of COM+ objects supported by the AppWizard.

WinRT

As mentioned before, for new project development, WinRT is your friend.


References