在HostApp和DLL之间预分配内存

时间:2010-04-12 09:57:53

标签: delphi memory dll

我有一个提供解码功能的DLL,如下所示:

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

HostApp调用“MyDecode”,并转入Source,SourceLen和Dest参数,DLL返回解码的Dest和DestLen。 问题是:HostApp无法知道解码的Dest长度,因此不知道如何预先分配Dest的内存。

我知道可以将“MyDecode”分成两个功能:

function GetDecodeLen (Source: PChar; SourceLen: Integer): Integer; stdcall;  // Return the Dest's length
function MyDecodeLen (Source: PChar; SourceLen: Integer; var Dest: PChar): Boolean; stdcall; 
但是,我的解码过程非常复杂,所以如果分成两个函数会影响效率。

有更好的解决方案吗?


是亚历山大,这可能是一个很好的解决方案。 HostApp代码:

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

DLL代码:

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;

也许我应该将Dest的类型改为Pointer。

哪个更好的分配内存方法? GetMem / New,或HeapAlloc?

4 个答案:

答案 0 :(得分:8)

您可以通过其他方式将“MyDecode”拆分为两个例程:

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

即。 - 你在MyDecode中分配内存而不是要求调用者这样做。

答案 1 :(得分:5)

您可以使用大多数Windows API使用的相同技术,也就是说,如果您的缓冲区不够大,该函数将返回所需缓冲区的大小。这样,您可以从调用函数中分配正确大小的缓冲区。

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;

编辑:正如mghie所建议的那样。由于参数是PCHAR,因此假设MyDecode返回的iLenNeeded将是Windows API所需的TCHAR数量(主要是?)标准。

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;

答案 2 :(得分:2)

另一种选择是向dll传递一个函数指针来分配内存。当需要内存时,dll会调用此函数,并且由于内存是使用应用程序的内存管理器分配的,因此应用程序可以释放它。

不幸的是,这并没有真正解决你的问题,只是将它移动到dll,然后必须弄清楚它需要多少内存。也许您可以使用存储在链表中的多个缓冲区,因此每次解码函数耗尽内存时,它只会分配另一个缓冲区。

答案 3 :(得分:1)

我不确定这是否适合您,但是(在此特定示例中)您可以使用WideString:

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

或者:

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

通过使用WideString,您可以完全避免内存分配问题。

为什么这会起作用?因为WideString是系统类型BSTR的别名。并且BSTR有特殊规则:其内存必须通过特定的系统内存管理器分配。即当您使用WideString时,Delphi会调用此系统内存管理器而不是它自己的内存管理器。由于系统内存管理器可以从每个模块访问(并且每个模块都是相同的) - 这意味着调用者(exe)和被调用者(DLL)都将使用相同的内存管理器,从而允许他们毫无问题地传递数据。 / p>

因此,您可以使用WideString并只生成结果而不必担心内存。请注意,WideString中的字符是unicode - 即2个字节。如果您正在使用D2007及更低版本,则转换ANSI< - > unicode会产生一些开销。这(通常)不是问题,因为典型的应用程序会进行大量的WinAPI调用 - 并且每个WinAPI调用意味着相同的ANSI< - > unicode转换(因为您正在调用A函数)。