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)
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:
uses
System.SysUtils, System.Classes, Vcl.AxCtrls;
procedure DoProcess(InStream, OutStream: TStream);
begin
//...do the actual processing here
end;
//wrapper export
procedure Process(AInStream: IStream; out AOutStream: IStream); safecall;
var
InStream, OutStream: TStream;
begin
InStream := TOleStream.Create(AInStream);
try
OutStream := TMemoryStream.Create;
try
DoProcess(InStream, OutStream);
AOutStream := TStreamAdapter.Create(OutStream, soOwned);
except
OutStream.Free;
raise;
end;
finally
InStream.Free;
end;
end;
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:
//wrapper export
procedure Process(AInStream, AOutStream: IStream); safecall;
var
InStream, OutStream: TStream;
begin
InStream := TOleStream.Create(AInStream);
try
OutStream := TOleStream.Create(AOutStream);
try
DoProcess(InStream, OutStream);
finally
OutStream.Free;
end;
finally
InStream.Free;
end;
end;
The EXE side might then look something like this:
//wrapper import
type
TDLLProcessProc = procedure(AInStream, AOutStream: IStream); safecall;
procedure Process(AInStream, AOutStream: TStream);
var
InStream, OutStream: IStream;
DLLProc: TDLLProcessProc;
Module: HMODULE;
begin
InStream := TStreamAdapter.Create(AInStream, soReference);
OutStream := TStreamAdapter.Create(AOutStream, soReference);
Module := LoadLibrary(MySuperLib);
if Module = 0 then RaiseLastOSError;
try
DLLProc := GetProcAddress(Module, 'Process');
if @DLLProc = nil then RaiseLastOSError;
DLLProc(InStream, OutStream);
finally
FreeLibrary(Module);
end;
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With