COM is quite a lot of things. Also threading model, date-time-currency formats, late binding/dynamic dispatch/scripting, type info both runtime and design time, IPC, RPC, security, and more.
> What you think of inheritance can be implemented with a struct containing nested structs of function pointers
What I think of inheritance in the context of COM is called “aggregation” and it’s more complex than that.
> It's not insurmountable
It’s not, but you’ll spend ~2x more time doing that compared to C++, and ~4x more time compared to VBScript or .NET.
Update: that 2x-4x is for implementation. For consumption C++ and .NET are close, but anything else is more complex due to FFI in between. C++ or VBScript don’t need FFI, .NET runtime is designed around many key parts of COM e.g. they have reused HRESULT’s and type info format.
Do they? COM in its essence defines two things:
1. A binary ABI for interoperability of components written in whatever language. 2. Reference-counting mechanism and "casting" mechanism (IUnknown).
MS has an article that shows how to build a COM component from the ground-up using only plain C structs containing function pointers (which is how vtables are implemented anyway: array of function pointers). What you think of inheritance can be implemented with a struct containing nested structs of function pointers. Etc.
It's not insurmountable, e.g., PyWin32 has utilities for both consuming and implementing COM components.