我有一个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?
答案 0 :(得分:7)
这个问题的典型方法是让应用程序分配内存,然后将其传递给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;
答案 1 :(得分:5)
DLL和你的主应用程序有两个不同的内存管理器,因此在DLL中分配内存但在主应用程序中释放它是不正确的,反之亦然。
您可以使用WideString类型从dll返回字符串或将其传递给dll - WideString是系统BSTR类型的包装器,WideString变量的内存由系统内存管理器自动分配。
另一个解决方案是使用SimpleShareMem而不是ShareMem(Delphi 2007及更旧版本) - 它的工作方式与ShareMem类似,但不需要任何类似borlnmm.dll的库来重新分发。
答案 2 :(得分:1)
当你从函数返回一个字符串作为PChar时,字符串被保存在堆栈中,这就是它有时被破坏的原因。我使用进程堆内存来返回字符串,或者使用指向全局缓冲区数组的指针。
此外,您可以使用内置汇编程序并执行此操作:
Function GetNameStr : PChar;
Asm
Call @OverText
DB 'Some text',0
@OverText:
Pop EAX
End;