Suppose I have a TMemoryStream
I need to pass to my DLL and get back TMemoryStream
(Bitmap stream) from the DLL.
I was thinking my DLL would have:
procedure Process(
InBuff: Pointer;
InBuffSize: Integer;
var OutBuff: Pointer;
var OutBuffSize: Integer
); stdcall;
The InBuff
is easy (I think). I pass TMemoryStream.Memory
and TMemoryStream.Size
.
Question is how do I allocate the OutBuff
in the DLL, and the caller application can convert it back to TMemoryStream
and later free that memory (by the caller application)?
The caller will use dynamic LoadLibrary
/FreeLibrary
each DLL call.
I would very much like a sample code. Hope I'm not being too rude.
Note 1: The caller application dose not know the output size, and assume it can not specify a MAX buff size.
Note 2: I am not sure about my DLL signature. Please forgive me if I did it wrong. I am Looking for a pattern that will work well (maybe not only for Delphi but for C++/C# Caller as well = Bonus for me)
Two obvious options, assuming the callee is to allocate the memory:
1. Use a shared heap
For instance you can use the COM heap. In the callee your write:
The caller destroys this with
CoTaskMemFree
. You can useLocalAlloc
, orHeapAlloc
if you prefer, it doesn't really matter.2. Use the callee's heap and export a deallocator
Here you use the native heap of the callee:
You also need to export a deallocator:
Another option that I rejected is to use a shared memory manager. I tend to avoid that because it constrains the caller to be a Delphi program.
To fill a stream from a buffer call
WriteBuffer
:where
Buff
is a pointer to the buffer.A slightly different approach is to wrap each memory stream up as a IStream, and pass around the resulting interface references. So, from the DLL's side:
Personally I like using safecall as well because it's an easy way to be exception-safe, but I guess that's a matter of taste.
Edit
A variant of the above is to have the caller provide both the stream to read and a stream to write to:
The EXE side might then look something like this:
Yes.
Given the signature of the DLL function you showed, you would not allocate the memory in the DLL at all. The caller has to allocate it. The caller can call
Process()
once to get the needed size, then allocate it, then callProcess()
again to fill it. This way, the caller is responsible for both allocating and freeing the memory. For example:If you actually want the DLL to allocate the memory, you have to change the signature of your DLL function to make
OutBuff
be avar
parameter. You also have to export an additional function so the DLL can free the memory that the DLL allocated. The benefit of this approach is that the caller would only have to callProcess()
once, and the DLL can decide how it wants to allocate and free the memory. For example: