Pre-allocate memory between HostApp and DLL

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?

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.


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;

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.


I'm not sure if this will suit you, but (in this particular example) you can use WideString:

function MyDecode(Source: PChar; SourceLen: Integer; out Dest: WideString): Boolean; stdcall;

Or:

function MyDecode(Source: PChar; SourceLen: Integer): WideString; stdcall;

By using WideString you can avoid memory allocation problems at all.

Why this will work? Because WideString is alias for system's type BSTR. And BSTR have special rule: its memory must be allocated via specific system memory manager. I.e. when you work with WideString, Delphi calls this system memory manager and not its own. Since system memory manager is accessible from each module (and it is the same for each module) - this means that both caller (exe) and callee (DLL) will use the same memory manager, thus allowing them to pass data without problems.

So, you can use WideString and just produce results without worrying about memory. Just note, that charaters in WideString are unicode - i.e. 2 bytes. You will have a little overhead with converting ANSI<->unicode, if you're using D2007 and below. This (usually) is not a problem, since typical application makes a tons of WinAPI calls - and each WinAPI call means the same ANSI<->unicode convertion (since you're calling A-functions).


Need Your Help

how to copy only body of pdf file using itextsharp or pdfsharp in C#

c# pdf parsing itextsharp pdfsharp

I am still waiting for the reply from you all. I really need your support.......plz

How to update progress dialog using counter from the task being run in Eclipse jobs API

java eclipse eclipse-plugin eclipse-rcp progressdialog

I'm using Eclipse jobs API for the first time. I have a plugin that needs to call a method from a jar file to process large data, so I need to show a progress dialog. My question is, how can I upda...