2012-04-26 6 views
5

Ho questa funzione nella nostra applicazione Delphi 7, che ha funzionato molto bene fino a includere FastMM4 v4.99 nel progetto. Una volta incluso, FastMM4 ha generato il seguente messaggio di errore: "FastMM ha rilevato un errore durante un'operazione di FreeMem. Il footer del blocco è stato danneggiato." L'esecuzione si ferma nella riga di FreeMem.FastMM4 dice "Il footer del blocco è stato danneggiato"

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; 

primo momento ho pensato che ci deve essere un bug nella funzione, ma è praticamente lo stesso di questo metodo di esempio TField.GetData in Delphi 7 aiuto:

{ 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; 

ho trovato sul Internet che il messaggio di errore sopra è spesso perché non c'è abbastanza spazio per i dati. Così ho aumentato la dimensione del blocco di memoria e il messaggio di errore è sparito e la funzione ha funzionato come previsto. Dopo qualche esperimento, ho capito che 2 byte sono necessari e sufficienti:

GetMem(p, aBinaryField.DataSize + 2); 

    FreeMem(p, aBinaryField.DataSize + 2); 

Anche se è risolto il mio problema, io non sono completamente rilassato con questa soluzione come io non conosco il suo sfondo. Sarebbe bello sapere il motivo di questo messaggio. FastMM4 ha bisogno di 2 byte aggiuntivi per il proprio piè di pagina?

AGGIORNAMENTO: Dopo aver trascorso un giorno con il debug, ora sono convinto che vi sia un errore nel metodo Delphi 7 TField.GetData. Scrive 2 byte zero oltre il blocco di memoria assegnato. Ho provato con e senza FastMM4 e sovrascrive in entrambi i casi (quindi non è un errore di FastMM4). Ho anche provato a digitare il campo come TVarBytesField, nessuna differenza. Ecco il codice che ho usato con commenti dettagliati, contenenti i risultati. La mia unica domanda rimanente: hanno corretto questo bug in Delphis successiva?

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; 

Dopo aver aggiunto un punto di interruzione di dati, lo stack di chiamate è:

@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 

risposta

5

FastMM alloca della memoria, alla fine del blocco che si alloca e scrive i valori conosciuta lì. Quindi, quando si rilascia un contratto, FastMM verifica che tali valori siano come previsto. In caso contrario, l'errore che viene visualizzato viene generato perché FastMM sa che il codice sta scrivendo oltre la fine del blocco di memoria.

Quindi, qualcosa all'interno del blocco try/finally sta scrivendo oltre la fine del blocco di memoria. Ed ecco perché l'aumento delle sue dimensioni rimuove l'errore FastMM. Senza vedere quel codice, non possiamo dire cosa è esattamente sbagliato e sarà necessario fare un po 'di debug per risolvere il problema.

Hai ragione ad essere preoccupato per la tua "soluzione". La prova e l'errore non sono mai un modo ragionevole per programmare. Devi scoprire perché il tuo programma sta scrivendo oltre la fine del blocco.

Un modo per farlo è impostare un punto di interruzione dei dati per l'indirizzo immediatamente oltre la fine di questo blocco. Ciò costringerebbe il debugger a rompere il codice che scrive oltre la fine del blocco.

Come parte, non è necessario passare il secondo parametro a FreeMem. In questo modo è più difficile mantenere il codice e non serve assolutamente a nulla. Passa il puntatore a FreeMem.

+0

Durante il debug ho commentato tutto tra try/finally eccetto la riga aBinaryField.GetData (p), quindi il mio codice finale sembrava esattamente come sopra. La rimozione del secondo parametro da FreeMem non ha risolto questo problema.Ma se FastMM scrive alcuni valori noti alla fine del mio blocco di memoria allocata, e poi riempio completamente il blocco con un altro dato (GetData fa questo IMHO) allora i valori noti verranno sovrascritti, anche se ho usato solo il blocco assegnato , vero? Quindi il +2 byte è per quei valori noti di FastMM. – Almandine

+0

Non ho detto che il secondo parametro di FreeMem avrebbe risolto il problema, solo che era inutile passarlo. Qualcosa sta scrivendo oltre la fine del blocco. –

+0

"Ma se FastMM scrive alcuni valori noti alla fine del mio blocco di memoria allocato, e poi riempio completamente il blocco con un altro dato (GetData esegue questo IMHO) allora i valori noti verranno sovrascritti, anche se ho usato solo il blocco assegnato, vero? " Non proprio. Quello che succede è che chiedi 10 byte, per esempio, ma FastMM assegna 14, per esempio. Ti dà un puntatore all'inizio di questo blocco che pensi abbia 10 byte in. Ma poi scrive valori noti nei 4 byte aggiuntivi alla fine. Se li sovrascrivi, FastMM ha trovato un bug. Qualcosa sta scrivendo oltre la fine. –