我在Delphi 7应用程序中具有此功能,在将FastMM4 v4.99包含到项目中之前,该功能运行良好。一旦包含,FastMM4会引发以下错误消息:“FastMM在FreeMem操作期间检测到错误。块页脚已损坏。” FreeMem行中的执行暂停。

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;

首先,我认为该函数中肯定有一个错误,但实际上与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错误)。我也尝试过将字段类型转换为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

最佳答案

FastMM在您分配的块的末尾分配一些内存,并在其中写入已知值。然后,当您取消分配时,FastMM检查这些值是否符合预期。如果不是,则将引发您看到的错误,因为FastMM知道您的代码正在写入,超出了内存块的末尾。

因此,try/finally块中的某些内容正在写入,超出了内存块的末尾。这就是为什么增加其大小会消除FastMM错误的原因。如果没有看到该代码,我们将无法分辨出确切的错误,您将需要进行一些调试才能解决该问题。

您完全有理由担心您的“解决方案”。反复试验从来都不是编程的合理方法。您必须找出为什么程序超出块末尾编写的原因。

一种方法是在该块末尾之后立即为地址设置数据断点。这将迫使调试器中断超出块末尾写入的代码。

顺便说一句,您不需要将第二个参数传递给FreeMem。这样做会使您的代码难以维护,并且毫无用处。仅将指针传递给FreeMem。

关于delphi - FastMM4说 "The block footer has been corrupted",我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10329940/

10-09 09:33