That very problem is what COM was designed to address. I'll leave it to my good friend Jeremiah Morrill to explain what the problem is:
I explained briefly how simple exporting regular “C” methods can be to
share code between DLLs and the application. I also explained how C++
classes get flattened and compiled down and also how they are seen to
your CPU (more or less). If C++ classes just get compiled to static
“C” methods, which can be exported from our DLL, is that not
sufficient to reuse and share classes across DLLs? Yes…but not
without some major caveats as this would come at a cost.
The first disadvantage of exporting a C++ class is you must use the same C runtime in all DLLs/exe involved. So if one DLL used MSVCRT7,
the caller of the DLL must be using MSVCRT7. The reason is different
versions of C runtimes manage the heap differently, so if DLL A tried
to free resources allocated by DLL B, memory corruption can occur.
The second disadvantage is the same compiler version must be used. This is because different compilers mangle names differently. Name
mangling differences among compilers ensures a DLL built with
Microsoft would have completely different symbol names than a DLL
built with GCC. So when linking of your code occurs, the linker might
be looking for “??4MyWidget…” when the export is really
Third, if a large change of the base classes in DLL A are made, even without breaking API, then DLL B that uses the base classes must
Forth [sic], if you take the last three disadvantages into consideration, we have a very tight coupling between our DLLs. This
inhibits reusability of the module.
Sometimes these issues are not an issue for a given project. This is
usually acceptable when the project is small, or an in house utility.
That’s totally fine. But let’s pretend Microsoft ignored these
problems. All developers would have to use the same compiler version.
Applications would have to be written for very specific versions of
Windows. This would be a complete nightmare for everyone.
Looking at those problems, COM is a simple solution for solving them. By exposing your C# dll to COM, you can call it from any other COM compliant language.