2012-04-26 19 views
5

Tôi có chức năng này trong ứng dụng Delphi 7 của chúng tôi, hoạt động rất tốt cho đến khi tôi đưa FastMM4 v4.99 vào dự án. Khi đã bao gồm, FastMM4 đã đưa ra thông báo lỗi sau: "FastMM đã phát hiện lỗi trong khi hoạt động FreeMem. Chân trang khối đã bị hỏng". Lệnh tạm dừng thực hiện trong dòng FreeMem.FastMM4 nói "Chân trang khối đã bị hỏng"

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; 

Trước tiên tôi nghĩ rằng phải có một lỗi trong chức năng, nhưng nó là thực tế giống như phương pháp này ví dụ TField.GetData trong Delphi 7 giúp đỡ:

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

tôi tìm thấy trên các internet rằng thông báo lỗi trên thường là do không có đủ không gian cho dữ liệu. Vì vậy, tôi tăng kích thước của khối bộ nhớ và thông báo lỗi biến mất và chức năng hoạt động như mong đợi. Sau một số thử nghiệm, tôi đã tìm ra rằng 2 byte là bắt buộc và đủ:

GetMem(p, aBinaryField.DataSize + 2); 

    FreeMem(p, aBinaryField.DataSize + 2); 

Mặc dù giải quyết được vấn đề của tôi, tôi không hoàn toàn thoải mái với giải pháp này vì tôi không biết nền tảng của nó. Nó sẽ được tốt đẹp để biết lý do của thông điệp này. FastMM4 có cần thêm 2 byte cho chân trang của riêng nó không?

CẬP NHẬT: Sau khi chi tiêu một ngày với gỡ lỗi, bây giờ tôi tin rằng có một lỗi trong phương pháp Delphi 7 TField.GetData. Nó viết 2 byte không vượt quá khối bộ nhớ được cấp phát. Tôi đã thử nó có và không có FastMM4, và nó ghi đè trong cả hai trường hợp (vì vậy nó không phải là một lỗi FastMM4). Tôi cũng đã thử với typecasting lĩnh vực như TVarBytesField, không có sự khác biệt. Đây là mã tôi đã sử dụng với các nhận xét chi tiết, chứa các kết quả. Câu hỏi còn lại duy nhất của tôi: họ đã sửa lỗi này sau trong Delphis chưa?

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; 

Sau khi thêm một Breakpoint dữ liệu, các cuộc gọi stack là:

@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 

Trả lời

5

FastMM phân bổ một số bộ nhớ ở phần cuối của khối mà bạn phân bổ và viết tiếng giá trị đó. Sau đó, khi bạn deallocate, FastMM kiểm tra rằng những giá trị như mong đợi. Nếu không, thì lỗi mà bạn thấy được nâng lên vì FastMM biết rằng mã của bạn đang viết vượt quá phần cuối của khối bộ nhớ.

Vì vậy, một cái gì đó bên trong khối thử/cuối cùng được viết vượt quá phần cuối của khối bộ nhớ. Và đó là lý do tại sao tăng kích thước của nó loại bỏ lỗi FastMM. Nếu không thấy mã đó, chúng tôi không thể nói chính xác những gì là sai và bạn sẽ cần phải thực hiện một số gỡ lỗi để giải quyết vấn đề.

Bạn hoàn toàn đúng khi được "giải pháp" quan tâm. Thử và sai không bao giờ là một cách hợp lý để lập trình. Bạn phải tìm hiểu lý do tại sao chương trình của bạn đang viết vượt quá phần cuối của khối.

Một cách để thực hiện điều đó là đặt điểm ngắt dữ liệu cho địa chỉ ngay ngoài phần cuối của khối này. Điều đó sẽ buộc trình gỡ lỗi phá vỡ mã viết vượt quá phần cuối của khối.

Ngoài ra, bạn không cần chuyển tham số thứ hai sang FreeMem. Làm như vậy làm cho mã của bạn khó khăn hơn để duy trì và phục vụ hoàn toàn không có mục đích. Chỉ chuyển con trỏ tới FreeMem.

+0

Trong khi gỡ lỗi, tôi đã nhận xét mọi thứ giữa lần thử/cuối cùng ngoại trừ hàng aBinaryField.GetData (p), vì vậy mã cuối cùng của tôi trông giống hệt như trên. Loại bỏ tham số thứ hai từ FreeMem không giải quyết được vấn đề này.Nhưng nếu FastMM viết một số giá trị đã biết ở cuối khối bộ nhớ được cấp phát của tôi, và sau đó tôi điền vào khối hoàn toàn bằng một dữ liệu khác (GetData thực hiện IMHO này) thì giá trị đã biết sẽ bị ghi đè, ngay cả khi tôi chỉ sử dụng khối được phân bổ , phải không? Vì vậy, byte +2 dành cho các giá trị FastMM đã biết đó. – Almandine

+0

Tôi đã không nói rằng tham số thứ hai để FreeMem sẽ giải quyết vấn đề, chỉ là nó là vô nghĩa để vượt qua nó. Một cái gì đó đang viết vượt quá khối kết thúc. –

+0

"Nhưng nếu FastMM viết một số giá trị đã biết ở cuối khối bộ nhớ được cấp phát của tôi, và sau đó tôi điền vào khối hoàn toàn bằng một dữ liệu khác (GetData thực hiện IMHO này) thì giá trị đã biết sẽ bị ghi đè, ngay cả khi tôi chỉ sử dụng phân bổ khối, phải không? " Không hẳn. Điều gì xảy ra là, bạn yêu cầu 10 byte, nói, nhưng FastMM phân bổ 14, nói. Nó cung cấp cho bạn một con trỏ đến đầu khối này mà bạn nghĩ có 10 byte. Nhưng sau đó nó ghi các giá trị đã biết trong 4 byte thêm vào cuối. Nếu ghi đè lên chúng, FastMM đã tìm thấy một lỗi. Một cái gì đó đang viết vượt ra ngoài kết thúc. –