Search code examples
delphidlldelphi-7

How do I pass and retrieve memory stream from my Application to/from DLL?


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)


Solution

  • 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:

    OutBuffSize := ...; // you know what this value is
    OutBuff := CoTaskMemAlloc(OutBuffSize);
    // populate the buffer
    

    The caller destroys this with CoTaskMemFree. You can use LocalAlloc, or HeapAlloc 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:

    OutBuffSize := ...; // you know what this value is
    GetMem(OutBuff, OutBuffSize);
    // populate the buffer
    

    You also need to export a deallocator:

    procedure DeallocateMemory(Ptr: Pointer); stdcall;
    begin
      FreeMem(Ptr);
    end;
    

    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:

    Stream.WriteBuffer(Buff^, BuffSize);
    

    where Buff is a pointer to the buffer.