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
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
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. –
"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. –