10

我有一个 DLL,其中有一个返回 pchar 的函数。(为了避免不得不使用 borlndmm)我最初所做的是将一个字符串转换为 pchar 并返回它

Result := pChar(SomeFuncThatReturnsString)

但我 90% 的时间都得到了预期的结果,而其他时候我什么也得不到。

然后我开始思考我需要为 pchar 分配内存,并且我最初的方式是让 pchar 指向内存,这并不总是在最初调用函数时存在。所以我现在有了这个

Result := StrAlloc(128);
Strcopy(Result,PAnsiChar(Hash(Hash(Code,1,128),2,128)));

但这让我不得不在我使用的程序端清理分配的内存

StrDispose(Pstr);    

所以 64 美元的问题是:从 DLL 中的函数返回 PChar 时是否必须分配内存,或者我可以将其转换为 PChar 吗?

4

3 回答 3

10

解决此问题的典型方法是让应用程序分配内存,然后将其传递给 DLL 进行填充(如果 DLL 允许应用程序查询它需要分配多少内存,这样它就不必过度-分配内存):

function GetAString(Buffer: PChar; BufLen: Integer): Integer; stdcall;
var
  S: String;
begin
  S := SomeFuncThatReturnsString;
  Result := Min(BufLen, Length(S));
  if (Buffer <> nil) and (Result > 0) then
    Move(S[1], Buffer^, Result * SizeOf(Char));
end;

这允许应用程序决定何时以及如何分配内存(堆栈与堆、重用内存块等):

var
  S: String;
begin
  SetLength(S, 256);
  SetLength(S, GetAString(PChar(S), 256));
  ...
end;

var
  S: String;
begin
  SetLength(S, GetAString(nil, 0));
  if Length(S) > 0 then GetAString(PChar(S), Length(S));
  ...
end;

var
  S: array[0..255] of Char;
  Len: Integer;
begin
  Len := GetAString(S, 256);
  ...
end;

如果这不适合您,那么您需要让 DLL 分配内存,将其返回给应用程序以供使用,然后让 DLL 导出一个附加函数,应用程序在完成后可以调用该函数以传递指针返回 DLL 进行释放:

function GetAString: PChar; stdcall;
var
  S: String;
begin
  S := SomeFuncThatReturnsString;
  if S <> '' then
  begin
    Result := StrAlloc(Length(S)+1);
    StrPCopy(Result, S);
  end else
    Result := nil;
end;

procedure FreeAString(AStr: PChar); stdcall;
begin
  StrDispose(AStr);
end;

var
  S: PChar;
begin
  S := GetAString;
  if S <> nil then
  try
    ...
  finally
    FreeAString(S);
  end;
end;
于 2010-11-24T19:51:14.167 回答
5

DLL 和您的主应用程序有两个不同的内存管理器,因此在 DLL 中分配内存但在主应用程序中释放它是不正确的,反之亦然。

您可以使用 WideString 类型从 dll 返回字符串或将其传递给 dll — WideString 是系统 BSTR 类型的包装器,WideString 变量的内存由系统内存管理器自动分配。

另一种解决方案是使用 SimpleShareMem 而不是 ShareMem(仅限 Delphi 2007 和更早版本)——它的工作方式与 ShareMem 类似,但不需要任何类似 borlnmm.dll 的库来重新分发。

于 2010-11-24T17:40:01.023 回答
1

当您从函数返回一个字符串作为 PChar 时,该字符串保存在堆栈中,这就是它有时会损坏的原因。我使用进程堆内存来返回字符串,或指向字符的全局缓冲区数组的指针。

您也可以使用内置汇编器并执行以下操作:

Function GetNameStr : PChar;
Asm
  Call   @OverText
  DB     'Some text',0
@OverText:
  Pop    EAX
End;
于 2010-11-26T05:29:22.377 回答