How to transfer and receive a memory stream from my application to / from a DLL?

Suppose I have one. TMemoryStreamI need to go to my DLL and go back TMemoryStream(Bitmap stream) from the DLL.

I thought my DLL would have:

procedure Process(
  InBuff: Pointer; 
  InBuffSize: Integer; 
  var OutBuff: Pointer; 
  var OutBuffSize: Integer
); stdcall;

InBuffeasy (i think). I pass TMemoryStream.Memoryand TMemoryStream.Size.

The question is, how can I allocate OutBuffin the DLL, and the receiving application can convert it back to TMemoryStream, and then free this memory (according to the statement of the caller)?

The caller will use dynamic LoadLibrary/ FreeLibraryevery DLL call.

I would really like the sample code. I hope I'm not too rude.

Note 1: The caller’s application extension does not know the size of the output and assumes that it cannot specify the size of the MAX buff.

2: DLL. , , . , (, Delphi, ++/# Caller) =/Bonus )

+2
3

, , :

1.

, COM. :

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

CoTaskMemFree. LocalAlloc HeapAlloc, , .

2. deallocator

:

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

deallocator:

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

, , - . , Delphi.

WriteBuffer:

Stream.WriteBuffer(Buff^, BuffSize);

Buff - .

+4

, IStream . , DLL:

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;

safecall, , , , .

Edit

, , :

//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;

EXE :

//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;
+7

InBuff ( ). TMemoryStream.Memory TMemoryStream.Size.

.

, OutBuff DLL, - TMemoryStream ( )?

DLL, , DLL. . Process() , , , Process(), . , . :

procedure Process(InBuff: Pointer; InBuffSize: Integer; OutBuff: Pointer; var OutBuffSize: Integer); stdcall;
begin
  //...
  if (OutBuf <> nil) then
  begin
    // copy no more than OutBuffSize bytes into OutBuf, and then
    // update OutBuffSize with the number of bytes actually copied...
    Move(..., OutBuf^, ...);
    OutBuffSize := ...;
  end else begin
    // update OutBuffSize with the number of bytes needed for OutBuff...
    OutBuffSize := ...;
  end;
  //...
end;

var
  InStream: TMemoryStream;
  OutStream: TMemoryStream;
  BuffSize: Integer;
begin
  InStream := TMemoryStream.Create;
  try
    // fill InStream as needed...

    BuffSize := 0;
    Process(InStream.Memory, InStream.Size, nil, BuffSize);

    OutStream := TMemoryStream.Create;
    try
      OutStream.Size := BuffSize;
      Process(InStream.Memory, InStream.Size, OutStream.Memory, BuffSize);
      // use OutStream as needed...
    finally
      OutStream.Free;
    end;
  finally
    InStream.Free;
  end;
end;

If you really want the DLL to allocate memory, you need to change the signature of your DLL function to OutBuffbe a parameter var. You must also export an additional function so that the DLL can free the memory allocated by the DLL. The advantage of this approach is that the caller would only need to be called once Process(), and the DLL can decide how he wants to allocate and free memory. For example:

procedure Process(InBuff: Pointer; InBuffSize: Integer; var OutBuff: Pointer; var OutBuffSize: Integer); stdcall;
begin
  //...
  OutBuffSize := ...;
  GetMem(OutBuf, OutBuffSize);
  Move(..., OutBuf^, OutBuffSize);
  //...
end;

procedure FreeProcessBuff(InBuff: Pointer); stdcall;
begin
  FreeMem(InBuff);
end;

type
  TMemoryBufferStream = class(TCustomMemoryStream)
  public
    constructor Create(APtr: Pointer; ASize: NativeInt);
  end;

procedure TMemoryBufferStream.Create(APtr: Pointer; ASize: NativeInt);
begin
  inherited Create;
  SetPointer(APtr, ASize);
end;

...

var
  InStream: TMemoryStream;
  OutStream: TMemoryBufferStream;
  Buff: Pointer;
  BuffSize: Integer;
begin
  InStream := TMemoryStream.Create;
  try
    // fill InStream as needed...

    Buff := nil;
    BuffSize := 0;
    Process(InStream.Memory, InStream.Size, Buff, BuffSize);
    try
      OutStream := TMemoryBufferStream.Create(Buff, BuffSize);
      try
        // use OutStream as needed...
      finally
        OutStream.Free;
      end;
    finally
      FreeProcessBuff(Buff);
    end;
  finally
    InStream.Free;
  end;
end;
+3
source

Source: https://habr.com/ru/post/1620735/


All Articles