在我们的Delphi 7应用程序中,我有这个函数,在不包括FastMM4 v4.99时很好用。一旦包括进来,FastMM4会引发以下错误消息:"快速内存管理器在FreeMem操作期间检测到错误。块底部已损坏。"执行会在FreeMem行中停止。
function BinaryFieldToArrayOfWord( aBinaryField : TVarBytesField;
out aArrValues : TArrWord ) : Boolean;
var
p : Pointer;
begin
if not aBinaryField.IsBlob then
begin
GetMem( p, aBinaryField.DataSize );
try
if aBinaryField.GetData( p ) then
begin
// do something
end;
finally
FreeMem( p, aBinaryField.DataSize );
end;
end; // if
end;
起初我认为这个函数一定有漏洞,但实际上它与Delphi 7帮助文档中的TField.GetData方法示例几乎相同:
{ Retrieve the "raw" data from Field1 }
with Field1 do
begin
if not IsBlob { this does not work for BLOB fields }
begin
{ Allocate space }
GetMem(MyBuffer, DataSize);
try
if not GetData(MyBuffer) then
MessageDlg(DisplayName + ' is NULL', mtInformation, [mbOK], 0)
else
{ Do something with the data };
finally
{ Free the space }
FreeMem(MyBuffer, DataSize);
end;
end;
end;
我在互联网上发现,上述错误消息通常是因为数据的空间不足所致。因此,我增加了存储器块的大小,错误消息消失了,函数按预期工作。经过一些实验,我发现只需要2个字节即可满足要求。
GetMem( p, aBinaryField.DataSize + 2 );
FreeMem( p, aBinaryField.DataSize + 2 );
虽然这个解决方案解决了我的问题,但我对它并不完全放心,因为我不知道它的背景。最好能知道这个消息的原因。FastMM4是否需要额外的2个字节作为自己的页脚?
更新:经过一天的调试,我现在相信Delphi 7 TField.GetData方法中存在一个错误。它会在分配的内存块之外写入2个零字节。我尝试了使用FastMM4和不使用FastMM4,两种情况下它都会发生覆盖(因此不是FastMM4的错误)。我还尝试将字段强制类型转换为TVarBytesField,没有任何区别。 这里是我用详细注释写的代码,其中包含结果。 我唯一剩下的问题是:他们在后来的Delphi中是否纠正了这个错误?
procedure TfrmMain_PBC_TH.btnDEBUGClick(Sender: TObject);
type
TArrBytes = array of Byte;
var
p : Pointer;
qryTest : TADOQuery;
begin
qryTest := TADOQuery.Create( Application );
try
// The type of the TQM_BinaryData.BinData column in the MSSQL database is a varbinary(7900).
// Load the #168 binary data record. It contains exactly 7900 bytes, and the value of each byte is 255
qryTest.Connection := MainConn;
qryTest.SQL.Add('SELECT [BinData] FROM [TQM_BinaryData] WHERE [Id] = 168');
qryTest.Open;
// Allocate the memory block for this.
GetMem( p, qryTest.FieldByName('BinData').DataSize );
// DataSize is 7902 because all TVarBytesFields have 2 byte prefix in Delphi, containing the data length.
// So the size of the allocated memory block is 7902 bytes - we are correct so far.
try
// Values of the first four bytes beyond the end of the memory block (memory thrash at this point) before GetData:
// TArrBytes(p)[7902] = 96
// TArrBytes(p)[7903] = 197
// TArrBytes(p)[7904] = 219
// TArrBytes(p)[7905] = 43
// Critical point: get the data from the field with the Delphi GetData method
qryTest.FieldByName('BinData').GetData( p );
// Values after GetData:
// TArrBytes(p)[0] = 220 TArrBytes(p)[0] and TArrBytes(p)[1] contains the length of the binary data
// TArrBytes(p)[1] = 30 it is correct as 30 * 256 + 220 = 7900
// TArrBytes(p)[2] = 255 actual data starts
// TArrBytes(p3[2] = 255
// ...
// TArrBytes(p)[7900] = 255
// TArrBytes(p)[7901] = 255 actual data ends
// TArrBytes(p)[7902] = 0 changed from 96!
// TArrBytes(p)[7903] = 0 changed from 197!
// TArrBytes(p)[7904] = 219 no change
// TArrBytes(p)[7905] = 43 no change
finally
// Here FastMM4 throws the block footer corrupt error because GetData modified the 2 bytes after the allocated memory block
FreeMem( p );
end;
qryTest.Close;
finally
qryTest.Free;
end;
end;
添加数据断点后,调用堆栈如下:
@FillChar(???,???,???)
TDataSet.DataConvert($7D599770,$12F448,$7D51F7F0,True)
VarToBuffer
TCustomADODataSet.GetFieldData($7D599770,$7D51F7F0,True)
TField.GetData($7D51F7F0,True)
TfrmMain_PBC_TH.btnDEBUGClick($7FF7A380)
TControl.Click
TButton.Click
qryTest.FieldByName('BinData').DataSize
的值是多少? - David Heffernan