考虑以下场景:
type
PStructureForSomeCDLL = ^TStructureForSomeCDLL;
TStructureForSomeCDLL = record
pName: PAnsiChar;
end
function FillStructureForDLL: PStructureForSomeDLL;
begin
New(Result);
// Result.pName := PAnsiChar(SomeObject.SomeString); // Old D7 code working all right
Result.pName := Utf8ToAnsi(UTF8Encode(SomeObject.SomeString)); // New problematic unicode version
end;
...code to pass FillStructureForDLL to DLL...
unicode 版本中的问题是,所涉及的字符串转换现在会在堆栈上返回一个新字符串,并在 FillStructureForDLL 调用结束时回收该字符串,从而使 DLL 留下损坏的数据。在旧的 D7 代码中,没有中间转换函数,因此没有问题。
我当前的解决方案是像下面这样的转换器函数,在我看来这太黑客了。有没有更优雅的方法来达到相同的结果?
var gKeepStrings: array of AnsiString;
{ Convert the given Unicode value S to ANSI and increase the ref. count
of it so that returned pointer stays valid }
function ConvertToPAnsiChar(const S: string): PAnsiChar;
var temp: AnsiString;
begin
SetLength(gKeepStrings, Length(gKeepStrings) + 1);
temp := Utf8ToAnsi(UTF8Encode(S));
gKeepStrings[High(gKeepStrings)] := temp; // keeps the resulting pointer valid
// by incresing the ref. count of temp.
Result := PAnsiChar(temp);
end;
最佳答案
一种方法可能是在问题成为问题之前解决它,我的意思是调整 SomeObject 类,以便为您维护 SomeString 的 ANSI 编码版本(ANSISomeString?)以及原始版本SomeString,将两者保持在 SomeString 属性的“setter”中(使用您已经执行的相同 UTF8 > ANSI 转换)。
在非 Unicode 版本的编译器中,使 ANSISomeString 只是 SomeString 字符串的“副本”,这当然不是副本,而只是 SomeString 上的附加引用计数。在 Unicode 版本中,它引用与原始 SomeString 具有相同“生命周期”的单独 ANSI 编码。
procedure TSomeObjectClass.SetSomeString(const aValue: String);
begin
fSomeString := aValue;
{$ifdef UNICODE}
fANSISomeString := Utf8ToAnsi(UTF8Encode(aValue));
{$else}
fANSISomeString := fSomeString;
{$endif}
end;
在您的 FillStructure... 函数中,只需更改代码以引用 ANSISomeString 属性 - 这完全独立于是否针对 Unicode 进行编译。
function FillStructureForDLL: PStructureForSomeDLL;
begin
New(Result);
result.pName := PANSIChar(SomeObject.ANSISomeString);
end;
关于delphi - 处理这个字符串问题的优雅方法。 (Unicode-PAnsiString 问题),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1452215/