I don't understand how the C# callee is able to allocate the memory, where the C++ callee wasn't able to?
You are looking for the wrong problem. The problem isn't "how the C# is able to allocate the memory". Anyone can allocate the memory... And in too many ways :-) The problem is always "how will you free the memory?", because freeing the memory is as much important as allocating the memory (not freeing the memory will cause memory leaks, that will make your program memory occupation go balloon, while freeing it with the wrong allocator won't free it at least (see then previous point), and will crash the program at worst).
Now, the OS (Windows for example) gives you some allocators (CoTaskMemAlloc
for example) that anyone can use, but the problem is that normally in C/C++ you'll use malloc
/new
. And the problem is here: these allocators could be "different" (distinct) between two dlls: two dlls compiled in C/C++ (but for example with different versions of Visual C++, or in debug vs release mode, or one using the runtime as a dll and the other linking directly the runtime, or compiled with different C compilers) will have distinct malloc
(and distinct new
in C++). Having distinct malloc
/new
they'll have distinct free
/delete
, so that the free
of one dll can't free the memory of the other dll.
Now... Using this signature:
void MyCPPDllFunction(char *out, int maxOutSize);
the caller has to allocate the memory... So the caller knows how to free the memory... No memory leak :-)
With this signature:
char *MyCPPDllFunction();
the callee has to allocate the memory... And now how can the caller free it? The callee could export another method:
void FreeMemoryAllocatedByMe(void *ptr)
{
free(ptr);
}
then the caller would call it and everything would be solved, because that free
would be the callee free
.
Now... In general, when C# marshals objects (and strings), it uses CoTaskMemAlloc
to allocate the memory, and it expects that if it receives memory, it has to free it with CoTaskMemFree
. So in you C++->C# example, the C++ should
CoTaskMemFree(output);