After someone brought this to my attention, and I did a bit of digging, it turns out that COM is almost but not quite identical to stdcall. The difference is that the C++ COM methods will always return a struct via a pointer, whereas stdcall, if the struct has only two pointer sized (or smaller) members, it will return it by value in eax and edx for 32-bit, or by value in rax for 64-bit. Although this is used very rarely, it is a huge problem when a COM function does return a struct, such as ID3D12Device::GetAdapterLuid.
Therefore I propose creating a new custom calling convention, based on stdcall, except it always returns aggregates by pointer.