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?
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.
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