2012-04-26 35 views
5

我在我們的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 

回答

5

FastMM在該塊的您分配結束分配一些存儲器和寫入已知值那裏。然後,當您釋放時,FastMM將檢查這些值是否與預期一致。如果不是,那麼你看到的錯誤會被提出,因爲FastMM知道你的代碼正在寫入超出內存塊的末尾。

因此,try/finally塊內的某些內容正在寫入超出內存塊的末尾。這就是爲什麼增加它的大小可以消除FastMM錯誤。如果沒有看到這些代碼,我們無法確定究竟是什麼錯誤,您需要進行一些調試來解決問題。

您完全有理由擔心您的「解決方案」。試驗和錯誤從來都不是合理的編程方式。你必須找出你的程序爲什麼寫在塊的末尾。

要做到這一點的一種方法是立即爲該塊末尾之外的地址設置數據斷點。這會迫使調試器打破超出塊結尾的代碼。

另外,您不需要將第二個參數傳遞給FreeMem。這樣做會使您的代碼難以維護,並且完全沒有任何用途。將指針傳遞給FreeMem。

+0

在調試過程中,我將try/finally除了aBinaryField.GetData(p)行之間的所有內容都註釋掉了,所以我最終的代碼看起來和上面完全一樣。從FreeMem中刪除第二個參數並沒有解決這個問題。但是,如果FastMM在分配的內存塊的末尾寫入了一些已知值,然後用另一個數據完全填充該塊(GetData執行此操作),則已知值將被覆蓋,即使我僅使用已分配的塊,不是嗎?所以+2字節是針對那些FastMM已知的值。 – Almandine 2012-04-26 12:31:38

+0

我沒有說FreeMem的第二個參數可以解決這個問題,只是通過它沒有意義。有些東西正在書寫之外。 – 2012-04-26 12:36:32

+0

「但是,如果FastMM在我分配的內存塊的末尾寫入了一些已知值,然後用另一個數據完全填充該塊(GetData執行此操作),則已知值將被覆蓋,即使我僅使用了分配的塊,不是嗎?「不是真的。比如說,你要求10個字節,但FastMM分配14個字節。它給你一個指向你認爲有10個字節的塊的開始的指針,但是它在最後將額外的4個字節寫入已知值。如果覆蓋它們,FastMM發現了一個錯誤。有些東西在寫完。 – 2012-04-26 12:49:00