Exactly. Using a pure C interface escapes COM's requirement to register every component (regsvr32) and its overblown "GUID for everything" model, and its baroque way of creating components (somewhat alleviated by various macros and templates, but still). Making an object oriented interface is slightly cumbersome, but you can do it by sending back and forth a cookie (void* or int) of your object as the first parameter.
This will also make your interface accessible to other languages, if the need arises, since the C ABI is a de-facto stable ABI on all platforms (disregarding library objects and issues).
Another alternative if you want to stick with C++: make up your own lightweight COM! I've done this successfully in my own code at work, it works great, and has been working for 8 years now.
This method allows people to write C++ components against my app without using COM and without having the exact same compiler versions and having symbol problems. It may seem like a lot of work but it really isn't.
1) Expose a pure virtual interface IFoo, that doesn't include any implementation. Once it is released, never touch it, only create new versions which inherit from it, IFoo2, IFoo3 etc.
2) Expose an extern "C" CreateFoo() that will return FooImpl : IFoo. Also a matching DeleteFoo(IFoo).
3) For all structs / classes you need to receive/send from IFoo methods, create another interface. This includes all C++ classes, including std::string, hide it behind IMyString or something. This is a minor inconvenience but it sidesteps all ABI incompatibilities.
4) Have all interfaces inherit from some IBaseXX which has a GetVersion() method
A component which uses this component could call this, and if it returns e.g. 2, then it can safely cast IFoo to IFoo2* and use IFoo2 methods. Else it can return an error message or use something from IFoo*
This relies on the fact that C++ vtable layout is essentially an ABI that will never change, at least under Windows, since the whole of COM relies on this and MS will never change it. Anything other than vtable layout in an object is subject to change, so the trick is to only have pure virtual interfaces.
I have no idea if this trick will also work on Linux, I don't know how stable GCC / Clang's vtable layout is from version to version, but I suspect it will.
This was taken from a CodeProject article I read a few years back, but I can't find it anymore... the closest I can find is [0] (DynObj - C++ Cross Platform Plugin Objects), but it is more complicated than what I read back then. I didn't use it, I wrote my own as I outlined above. Like I said, it isn't really that complicated.
> Using a pure C interface escapes COM's requirement to register every component (regsvr32) [...] Another alternative if you want to stick with C++: make up your own lightweight COM
Or you can combine both: use a pure C interface which returns COM objects. That way, you can keep using COM without having to register anything with regsvr32 or similar.
> I have no idea if this trick will also work on Linux, I don't know how stable GCC / Clang's vtable layout is from version to version
Recent (as in, since around the turn of the millennium) GCC and clang on Linux use a standard vtable layout, defined by the "Itanium ABI" (originally created for Intel's IA-64, but like UEFI and GPT, became a standard across all architectures), so it's also very stable.
Itanium ABI is somewhat stable. As in there exists a chance that two binary files compiled with exact same set of libraries and headers will be compatible, which is something that will get broken the moment two files were compiled with different switches in GCC that do not technically change things at Itanium ABI levels, but cause incompatibilities in various standard types.
Ah! Yeah, I've completely forgotten that the Itanium ABI also guarantees a standard vtable layout. Thanks!
Were starting to convert my app from a Windows-only app to a cross platform one, so this makes me happy that my "COM lite" scheme would also be stable on linux.
As for returning pure COM objects through a C interface: Yeah, I've considered that, but like I wrote in my comment below to pjmlp, I don't like COM personally, and I want to simplify writing the components' code as much as possible, since the team which writes them isn't a team of software developers but rather in a supporting role. They do know basic C++/C# but don't know COM.
Also getting rid of COM would be the first thing done for cross platform support, anyway.
Due to experience trying to link some code, especially C++, I'd probably rather start by implementing a simplified COM system first rather than dealing with extern C
I was aware of registration free COM, and I did consider it, in fact I tried it out several times during the last decade.
However in my experience it was always hard to get working properly, and it was always poorly documented. I'm talking primarily about the XML assembly manifest, which is very easy to get wrong.
In fact I remember vaguely the only complete documentation & example I could find at the time was an article in MSDN magazine, but now I can't find it, only bits and pieces of information scattered around, nothing in MSDN except a vague article. Most references I can find are for consuming dotnet COM objects and I also need to consume components written in C++. So the situation has gotten worse, documentation-wise.
Another couple of points:
1) Personally I don't want to use COM at all, I think it's too complex. I think it's really a brilliant idea but wrapped with over-engineered, idiosyncratic idioms and API. I tried to distill it to the minimum required by me for my "COM lite" above.
2) I'm not the one creating those components, I'm in charge of the consuming application. The people creating those components (in house) are essentially my support people, they're not really professional developers, and get confused with anything slightly more complex than plain C++ code. None of them has ever learned anything about COM, I'm the only one who knows all about it. Meaning I have to support them when things go wrong with COM registration or when they compile their code and it doesn't work. So I'm on a mission to get rid of all COM dependencies in my application, and replace them either with plain DLLs with C API, or (in one specific case) with my "COM-Lite" I outlined above.
For in-process DLL-based servers, you could try to implement CoCreateClass (IIRC) manually and bypass GUID-to-server lookup and go directly for loading a DLL and calling the method for getting the object from it.
Probably, although I tend to stay away of such solutions, as they eventually turn into headaches to sort out in some machine, where the hack doesn't work as expected.
This will also make your interface accessible to other languages, if the need arises, since the C ABI is a de-facto stable ABI on all platforms (disregarding library objects and issues).
Another alternative if you want to stick with C++: make up your own lightweight COM! I've done this successfully in my own code at work, it works great, and has been working for 8 years now.
This method allows people to write C++ components against my app without using COM and without having the exact same compiler versions and having symbol problems. It may seem like a lot of work but it really isn't.
1) Expose a pure virtual interface IFoo, that doesn't include any implementation. Once it is released, never touch it, only create new versions which inherit from it, IFoo2, IFoo3 etc.
2) Expose an extern "C" CreateFoo() that will return FooImpl : IFoo. Also a matching DeleteFoo(IFoo).
3) For all structs / classes you need to receive/send from IFoo methods, create another interface. This includes all C++ classes, including std::string, hide it behind IMyString or something. This is a minor inconvenience but it sidesteps all ABI incompatibilities.
4) Have all interfaces inherit from some IBaseXX which has a GetVersion() method A component which uses this component could call this, and if it returns e.g. 2, then it can safely cast IFoo to IFoo2* and use IFoo2 methods. Else it can return an error message or use something from IFoo*
This relies on the fact that C++ vtable layout is essentially an ABI that will never change, at least under Windows, since the whole of COM relies on this and MS will never change it. Anything other than vtable layout in an object is subject to change, so the trick is to only have pure virtual interfaces.
I have no idea if this trick will also work on Linux, I don't know how stable GCC / Clang's vtable layout is from version to version, but I suspect it will.
This was taken from a CodeProject article I read a few years back, but I can't find it anymore... the closest I can find is [0] (DynObj - C++ Cross Platform Plugin Objects), but it is more complicated than what I read back then. I didn't use it, I wrote my own as I outlined above. Like I said, it isn't really that complicated.
[0] https://www.codeproject.com/Articles/20648/DynObj-C-Cross-Pl...