2012-04-26 9 views
5

Mam tę funkcję w naszej aplikacji Delphi 7, która działała bardzo dobrze, dopóki nie włączyłem FastMM4 v4.99 do projektu. Po włączeniu FastMM4 podniósł następujący komunikat o błędzie: "FastMM wykrył błąd podczas operacji FreeMem. Stopka bloku została uszkodzona." Wykonanie zatrzymuje się w linii FreeMem.FastMM4 mówi "Stopka bloku została uszkodzona"

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; 

Najpierw myślałem, że to musi być błąd w funkcji, ale jest praktycznie taki sam jak ten przykładowy sposób TField.GetData w pomocy Delphi 7:

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

znalazłem w internet, że powyższy komunikat o błędzie jest często, ponieważ nie ma wystarczająco dużo miejsca na dane. Dlatego zwiększyłem rozmiar bloku pamięci i komunikat o błędzie zniknął, a funkcja działała zgodnie z oczekiwaniami. Po pewnym eksperymencie, zorientowałem się, że wymagane jest 2 bajty i tyle:

GetMem(p, aBinaryField.DataSize + 2); 

    FreeMem(p, aBinaryField.DataSize + 2); 

Mimo to rozwiązać mój problem, nie jestem całkowicie zrelaksowany z tego rozwiązania, jak nie znam jego tło. Byłoby miło poznać przyczynę tej wiadomości. Czy FastMM4 potrzebuje dodatkowych 2 bajtów dla własnej stopki?

AKTUALIZACJA: Po spędzeniu jednego dnia z debugowaniem, jestem teraz przekonany, że istnieje błąd w metodzie Delphi 7 TField.GetData. Zapisuje 2 zero bajtów poza przydzielonym blokiem pamięci. Próbowałem go z FastMM4 i bez niego, i to zastępuje w obu przypadkach (więc nie jest to błąd FastMM4). Próbowałem również z typowaniem linii pola jako TVarBytesField, bez różnicy. Oto kod, którego użyłem ze szczegółowymi komentarzami, zawierającymi wyniki. Moje jedyne pytanie: czy poprawili ten błąd w późniejszym 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; 

Po dodaniu danych Breakpoint wywołanie stosu jest:

@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 

Odpowiedz

5

FastMM przydziela trochę pamięci na końcu bloku, który można przydzielić i pisze znany wartości istnieje. Następnie, po zwolnieniu, FastMM sprawdza, czy te wartości są zgodne z oczekiwaniami. Jeśli nie, to pojawia się błąd, ponieważ FastMM wie, że twój kod pisze poza końcem bloku pamięci.

Coś wewnątrz bloku try/finally zapisuje się poza końcem bloku pamięci. I dlatego zwiększenie jego rozmiaru usuwa błąd FastMM. Nie widząc tego kodu, nie możemy stwierdzić, co dokładnie jest nie tak i będziesz musiał przeprowadzić pewne debugowanie, aby rozwiązać problem.

Masz całkowitą rację, jeśli chodzi o twoje "rozwiązanie". Próba i błąd nigdy nie jest rozsądnym sposobem programowania. Musisz dowiedzieć się, dlaczego Twój program pisze poza końcem bloku.

Jednym ze sposobów jest ustawienie punktu przerwania danych dla adresu bezpośrednio po zakończeniu tego bloku. Zmusiłoby to debugger do złamania kodu, który pisze poza końcem bloku.

Odkładając na bok, nie trzeba przekazywać drugiego parametru do FreeMem. Dzięki temu Twój kod jest trudniejszy w utrzymaniu i nie służy niczemu. Przekaż tylko wskaźnik do FreeMem.

+0

Podczas debugowania skomentowałem wszystko między próbą/końcem z wyjątkiem wiersza aBinaryField.GetData (p), więc mój ostatni kod wyglądał dokładnie tak, jak powyżej. Usunięcie drugiego parametru z FreeMem nie rozwiązało tego problemu.Ale jeśli FastMM zapisze jakieś znane wartości na końcu mojego przydzielonego bloku pamięci, a następnie wypełni całkowicie blok innymi danymi (GetData wykona to IMHO), wtedy znane wartości zostaną nadpisane, nawet jeśli użyłem tylko przydzielonego bloku , prawda? Tak więc bajt +2 jest dla tych znanych wartości FastMM. – Almandine

+0

Nie powiedziałem, że drugi parametr do FreeMem rozwiąże problem, ale nie ma sensu go przekazywać. Coś pisze poza końcem bloku. –

+0

"Ale jeśli FastMM zapisze jakieś znane wartości na końcu mojego przydzielonego bloku pamięci, a następnie wypełni całkowicie blok innym danymi (GetData wykona to IMHO), wtedy znane wartości zostaną nadpisane, nawet jeśli użyłem tylko przydzielony blok, prawda? " Nie całkiem. To, co się dzieje, to 10 bajtów, powiedzmy, ale FastMM przydziela 14, powiedzmy. Daje ci wskaźnik do początku tego bloku, który twoim zdaniem ma 10 bajtów. Ale potem zapisuje znane wartości w dodatkowych 4 bajtach na końcu. Jeśli je zastąpisz, FastMM znalazł błąd. Coś pisze poza końcem. –