我在我們的Delphi 7應用程序中有這個功能,直到我將FastMM4 v4.99包含到項目中後,它才運行良好。一旦包含,FastMM4提出了以下錯誤消息:「FastMM在FreeMem操作過程中檢測到錯誤,塊頁腳已損壞。」執行暫停在FreeMem行中。FastMM4說「塊腳註已損壞」
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;
首先,我認爲必須有功能的bug,但實際上是一樣在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進行類型化,沒有區別。 以下是我使用包含結果的詳細註釋的代碼。 我唯一剩下的問題是:他們在後來的Delphis中糾正了這個錯誤嗎?
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
在調試過程中,我將try/finally除了aBinaryField.GetData(p)行之間的所有內容都註釋掉了,所以我最終的代碼看起來和上面完全一樣。從FreeMem中刪除第二個參數並沒有解決這個問題。但是,如果FastMM在分配的內存塊的末尾寫入了一些已知值,然後用另一個數據完全填充該塊(GetData執行此操作),則已知值將被覆蓋,即使我僅使用已分配的塊,不是嗎?所以+2字節是針對那些FastMM已知的值。 – Almandine 2012-04-26 12:31:38
我沒有說FreeMem的第二個參數可以解決這個問題,只是通過它沒有意義。有些東西正在書寫之外。 – 2012-04-26 12:36:32
「但是,如果FastMM在我分配的內存塊的末尾寫入了一些已知值,然後用另一個數據完全填充該塊(GetData執行此操作),則已知值將被覆蓋,即使我僅使用了分配的塊,不是嗎?「不是真的。比如說,你要求10個字節,但FastMM分配14個字節。它給你一個指向你認爲有10個字節的塊的開始的指針,但是它在最後將額外的4個字節寫入已知值。如果覆蓋它們,FastMM發現了一個錯誤。有些東西在寫完。 – 2012-04-26 12:49:00