It is very common to allocate a block of memory during the execution of COM server, and then pass that memory block to the client through an ouput parameter. Then it is the client obligation to free that memory, with methods such as CoTaskMemFree().
Question is, where is this block of memory allocated? Supposing that the COM server and COM client are in different processes, in order for the client to access that memory block, it SHOULD be allocated in the client's process address space. But is that true? I heard that COM has a "Task Memory Allocator". But I know little about it.
Just some wild guesses:
First, it is the COM server allocate the memory at the request of the COM client, with CoTaskMemAlloc().
And then, COM client get that piece of memory, use it, and free it with CoTaskMemFree().
So the "Task Memory Allocator" must keep track of both the client and server processes. Otherewise, it won't know who(the server) did the memory allocation action and who(the client) should be given that memory. Then, the allocated memory will be somehow injected to the client's process address space.
Could anyone shed some light on this topic?
Well, "task memory allocator" is a COM-owned allocator that exposes those CoTaskMem*
functions. Now suppose the client and the server are in different processes and the server uses CoTaskMemAlloc()
to allocate an "out" parameter. How does it get to the client?
COM subsytem with marshalling does that. The server allocates memory and returns control from its COM method implementation. COM subsystem now has to marshal the call results to the client. It simply takes ownership of that memory and marshals it to the client. The client allocates its own block on its (the client) heap, data is copied to the client, the block on the server is freed. The client get ownership of the block and must free it later otherwise the block is leaked.
So the client and the server address spaces are always separated and no direct data access happens. Each uses its own memory allocator, marshalling kicks in in the middle to make the client allocate memory and make the server free memory so that the client gets ownership of a legally allocated block and the server releases ownership of the block it itself allocated.
So to the client it almost looks like the server allocated the memory and returned it to the client. The one notable exception is that logical addresses are allowed to differ - say server allocated memory at address 0x10001000
and returned that address together with the block. The client is not guaranteed to get the block at the same logical address - the address will be up to the client side allocator.
If you allocate a block of memory using CoTaskMemAlloc, that memory ends up being allocated using the default per-process heap (source).
The difference between using CoTaskMemAlloc and any other allocator however is that this allocation is COM aware, which means that COM is able to marshal this memory across process boundaries (e.g. by copying the memory) when required.
The question of where the memory block is allocated by the Task Memory Allocator is an implementation detail, and I don't think that it's relevant for you to know. However, the important thing is, that this Memory Allocator gives you an interface and mechanism by which you can allocate and deallocate memory across process boundaries.
When you allocate memory which a certain mechanism, whether it be new
or CoTaskMemAlloc
, you have to use the corresponding deallocation mechanism. So with new
you use delete
and with CoTaskMemAlloc
you would use CoTaskMemFree
The OLE Allocator will take care of allocating and freeing memory correctly, even if you allocate in process X, pass to process Y and then deallocate.
The point is that you use the same mechanism.