Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pre-allocate memory between HostApp and DLL

Tags:

memory

dll

delphi

I have a DLL which provided a decoding function, as follows:

function MyDecode (Source: PChar; SourceLen: Integer; var Dest: PChar; DestLen: Integer): Boolean; stdcall; 

The HostApp call "MyDecode", and transfer into the Source, SourceLen and Dest parameters, the DLL returns decoded Dest and DestLen. The problem is: The HostApp impossible to know decoded Dest length, and therefore would not know how to pre-allocated Dest's memory.

I know that can split "MyDecode" into two functions:

function GetDecodeLen (Source: PChar; SourceLen: Integer): Integer; stdcall;  // Return the Dest's length
function MyDecodeLen (Source: PChar; SourceLen: Integer; var Dest: PChar): Boolean; stdcall; 

But, My decoding process is very complicated, so if split into two functions will affect the efficiency.

Is there a better solution?


Yes Alexander, this may be a good solution. HostApp code:

//... 
MyDecode(....) 
try 
  // Use or copy Dest data 
finally 
  FreeDecodeResult(...) 
end;

DLL code:

function MyDecode(...): Boolean;
begin
  // time-consuming calculate

  // Allocate memory
  GetMem(Dest, Size);   
  // or New()?
  // or HeapAlloc()?
end;

procedure FreeDecodeResult(Dest: PChar);
begin
  FreeMem(Dest);
  // or Dispose(Dest); ?
  // or HeapFree(Dest); ?
end;

Maybe I should change Dest's type to Pointer.

Which is a better allocate memory method? GetMem/New, or HeapAlloc?

like image 687
Leo Avatar asked Apr 12 '10 09:04

Leo


3 Answers

You can split "MyDecode" into two routines by other way:

function  MyDecode(Source: PChar; SourceLen: Integer; out Dest: PChar; out DestSize: Integer): Boolean; stdcall;
procedure FreeDecodeResult(Dest: PChar); stdcall;

I.e. - you allocate memory in MyDecode instead of asking a caller to do that.

like image 200
Alex Avatar answered Sep 20 '22 01:09

Alex


You could use the same technique that most Windows API use, that is, if your buffer is not large enough, the function returns the size of the buffer needed. That way, you can allocate a buffer of the right size from the calling function.

function MyDecode (Source: PChar; SourceLen: Integer; Dest: PChar; var Len: Integer): Boolean; stdcall;

procedure SomeProc;
var iSourceLen, iLenNeeded : Integer;
    pSource, pDest : Pointer;
begin
  MyDecode(pSource, iSourceLen, nil, iLenNeeded);
  GetMem(pDest,iLenNeeded);
  try
    MyDecode(pSource, iSourceLen,pDest, iLenNeeded);
  finally
    FreeMem(pDest);
  end;
end;

EDIT: As suggested by mghie. Since parameter is PCHAR, it will be assumed the iLenNeeded returned by MyDecode will be the number of TCHAR required as is (mostly?) standard by the windows API.

function SomeProc(sEncode : String) : string;
var iLenNeeded : Integer;
begin
  MyDecode(PChar(sEncode), Length(sEncode), nil, iLenNeeded);
  SetLength(Result, iLenNeeded);  //if iLenNeeded include a null-terminating character, you can use (iLenNeeded - 1) instead
  if not MyDecode(PChar(sEncode), Length(sEncode), PChar(Result), iLenNeeded) then
    SetLength(Result, 0);
end;
like image 33
Ken Bourassa Avatar answered Sep 18 '22 01:09

Ken Bourassa


Another option is to pass into the dll a function pointer to allocate memory. The dll calls this function when it needs memory and since the memory is allocated using the application's memory manager the application can just free it.

Unfortunately this does not really solve your problem but only moves it to the dll which must then figure out how much memory it needs. Maybe you could use multiple buffers stored in a linked list so every time the decode function runs out of memory it just allocates another buffer.

like image 29
dummzeuch Avatar answered Sep 21 '22 01:09

dummzeuch