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;
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.
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;
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).