2012-04-26 5 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; 

Сначала я думал, что там должна быть ошибка в функции, но это практически то же самое как в этом примере TField.GetData метода в 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; 

я нашел на в Интернете, что указанное выше сообщение об ошибке часто связано с тем, что для данных недостаточно места. Поэтому я увеличил размер блока памяти, и сообщение об ошибке исчезло, и функция работала должным образом. После некоторого опыта, я понял, что 2 байта требуется и достаточно:

GetMem(p, aBinaryField.DataSize + 2); 

    FreeMem(p, aBinaryField.DataSize + 2); 

Хотя решить мою проблему, я не полностью расслаблен с этим решением, так как я не знаю, его фон. Было бы неплохо узнать причину этого сообщения. Требуется ли FastMM4 дополнительные 2 байта для собственного нижнего колонтитула?

UPDATE: После проведенного дня с отладкой, я теперь считаю, что есть ошибка в методе Delphi 7 TField.GetData. Он записывает 2 нулевых байта за выделенный блок памяти. Я пробовал его с 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; 

После добавления Breakpoint данных, стек вызовов:

@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 делает это IMHO), тогда известные значения будут перезаписаны, даже если бы я использовал только выделенный блок , не так ли? Таким образом, +2 байт предназначен для известных значений FastMM. – Almandine

+0

Я не сказал, что второй параметр FreeMem решит проблему, просто чтобы передать ее было бессмысленно. Что-то пишет за пределами блока. –

+0

«Но если FastMM записывает некоторые известные значения в конце выделенного блока памяти, а затем заполняю блок полностью другими данными (GetData делает это IMHO), тогда известные значения будут перезаписаны, даже если бы я использовал только выделенный блок, не так ли? " На самом деле, нет. Что происходит, вы спрашиваете 10 байт, скажем, но FastMM выделяет 14, скажем. Он дает вам указатель на начало этого блока, который, по вашему мнению, имеет 10 байтов. Но затем он записывает известные значения в дополнительные 4 байта в конце. Если перезаписать их, FastMM обнаружил ошибку. Что-то написано за гранью. –

Смежные вопросы