尝试将Delphi 2007
项目移至XE4
。在Delphi 2007
我使用的函数直接使用Indy
从套接字读取字节数组。我将AnsiString
转换为此函数的var参数的字节数组:
var data:AnsiString;
AContext.Connection.IOHandler.ReadBytes(TIDBytes(Data), PacketLength-PacketLengthDelta-1, False);
在Dlphi XE
中,当我尝试将Data
连接到另一个字符串时,我遇到access violation
错误。
现在我试图用更简单的代码模拟这个问题:
TIdBytes = array of Byte;
procedure fill(var b: TIDBytes);
begin
setlength(b,5);
b[0]:=61;
b[1]:=61;
b[2]:=61;
b[3]:=61;
b[4]:=61;
//original function used move function
end;
procedure TMainForm.FormCreate(Sender: TObject);
var s: ansistring ;
begin
fill( TIDBytes(s) );
Showmessage(s);
end;
现在我希望在消息框中看到====
之类的东西,但我得到了空的。我认为XE AnsiString的作用与Delphi 2007 Ansistring相同,你可以在两种情况下都像字节数组一样使用它们。
用字节问题解决填充AnsiString的最佳方法是什么?
答案 0 :(得分:5)
将AnsiString
强制转换为字节数组永远无效。那段代码总是坏了,你很幸运(或者根据你的观点不幸)。
托管字符串类型,就像动态数组一样,具有额外的信息有效负载,元数据,存储在数据有效负载之前。此元数据包括引用计数,长度等。但字符串的元数据与动态数组的元数据不同。简单地说,字符串不是动态数组。您的重新解释演员表完全无效。它在旧版本的Delphi中无效,在现代版本中它也同样无效。
真正发生的事情是,随着Unicode支持的引入,元数据的大小已经改变。 AnsiString
的元数据已经扩展。例如,它现在包含字符串的代码页。
现在,当您调用SetLength
时,会分配足够大的元数据和有效负载的内存块。假设在地址P处分配了内存。您的变量(字符串或动态数组)保存的地址设置为P +元数据大小。当您解除分配对象时,系统将移至P - 元数据大小并使用该地址调用FreeMem
。这是踢球者。分配时使用动态数组元数据的大小,但是在解除分配时使用字符串元数据的大小。结果?繁荣!您刚刚在无效地址上调用FreeMem
,该地址尚未分配给您。
处理这个问题的正确方法是给Indy函数提供它想要的东西。即一个字节数组。如果需要传输到字符串变量,请将字节数组的内容复制到新字符串中。例如,使用TEncoding.Default.GetString()
。
答案 1 :(得分:1)
使用您的填充程序和您的TIDBytes定义:
procedure Test;
var
s: ansistring;
b: TIDBytes;
begin
fill(b);
s := StringOf(TArray<byte>(b));
ShowMessage(s);
end;
PS:@David答案是正确的,但你可以使用directy字节数组,然后使用StringOf来获取字符串/ ansistring