При загрузке картинки с помощью контекстного меню (пункт Из файла...), возникает AccessViolation.
Ehlib 9.5.008
Embarcadero® Delphi 10.1 Berlin Version 24.0.25048.9432
Installed Updates Delphi 10.1 Berlin and C++ Builder 10.1 Berlin Update 2
БД MSSQL
Используется связка TFDConnection ==> TFDQuery ==> TDataSource ==> TDBImageEh.
Таблица
Код:
CREATE TABLE [dbo].[INT_PODR](
[PODR_ID] [int] IDENTITY(1,1) NOT NULL,
[PODR_IMAGE2] [varbinary](max) NULL,
CONSTRAINT [aaaaaINT_PODR_PK] PRIMARY KEY NONCLUSTERED
(
[PODR_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
CallStack
- DBCtrlsEh.TCustomDBImageEh.DataChange(???)
- DBCtrlsEh.TFieldDataLinkEh.RecordChanged(???)
- Data.DB.TDataLink.DataEvent(???,???)
- DBCtrlsEh.TFieldDataLinkEh.DataEvent(deFieldChange,2120935728)
- Data.DB.TDataSource.NotifyLinkTypes(deFieldChange,2120935728,True)
- Data.DB.TDataSource.NotifyDataLinks(deFieldChange,2120935728)
- Data.DB.TDataSource.DataEvent(deFieldChange,2120935728)
- Data.DB.TDataSet.DataEvent(deFieldChange,2120935728)
- FireDAC.Comp.DataSet.TFDDataSet.DataEvent(deFieldChange,2120935728)
- FireDAC.Comp.DataSet.TFDBlobStream.Destroy
- System.TObject.Free
- Data.DB.TBlobField.LoadFromStreamPersist(TJPEGImage($7E3D61E4) as IStreamPersist)
- Data.DB.TBlobField.Assign(???)
- DBCtrlsEh.TCustomDBImageEh.UpdateData(???)
- DBCtrlsEh.TCustomDBImageEh.MenuItemLoad(???)
- Vcl.Menus.TMenuItem.Click
15.
Код:
procedure TCustomDBImageEh.MenuItemLoad(Sender: TObject);
var
OpenDialog: TOpenPictureDialog;
begin
OpenDialog := TOpenPictureDialog.Create(Self);
try
OpenDialog.Title := SLoadPictureTitle;
if OpenDialog.Execute then
begin
if FDataLink.Edit then
begin
Picture.LoadFromFile(OpenDialog.Filename); //<== В Picture.Graphic картинка
UpdateData(nil); // <==
end;
end;
finally
OpenDialog.Free;
end;
end;
14.
Код:
procedure TCustomDBImageEh.UpdateData(Sender: TObject);
begin
if not DataLink.DataIndepended then
DataLink.Field.Assign(Picture.Graphic);
end;
13.
Код:
procedure TBlobField.Assign(Source: TPersistent);
var
StreamPersist: IStreamPersist;
begin
if Source is TBlobField then
LoadFromBlob(TBlobField(Source))
else if Source is TStrings then
LoadFromStrings(TStrings(Source))
else if SupportsStreamPersist(Source, StreamPersist) then // <== Получаем интерфейс от Picture.Graphic
LoadFromStreamPersist(StreamPersist) // <==
else
inherited Assign(Source);
end;
12.
Код:
procedure TBlobField.LoadFromStreamPersist(StreamPersist: IStreamPersist);
var
Header: TGraphicHeader;
BlobStream: TStream;
begin
BlobStream := DataSet.CreateBlobStream(Self, bmWrite); // <== TFDDataSet.CreateBlobStream
try
if GraphicHeader and (DataType = ftGraphic) or (DataType = ftTypedBinary) then
begin
Header.Count := 1;
Header.HType := $0100;
Header.Size := 0;
BlobStream.Write(Header, SizeOf(Header));
StreamPersist.SaveToStream(BlobStream);
Header.Size := BlobStream.Position - SizeOf(Header);
BlobStream.Position := 0;
BlobStream.Write(Header, SizeOf(Header));
end else
StreamPersist.SaveToStream(BlobStream);
finally
BlobStream.Free; // <== TFDBlobStream.Destroy;
end;
end; // <== Тут будет AV
Код:
function TFDDataSet.CreateBlobStream(Field: TField; Mode: TBlobStreamMode): TStream;
begin
Result := TFDBlobStream.Create(Field, Mode);
end;
10.
Код:
destructor TFDBlobStream.Destroy;
var
oRow: TFDDatSRow;
oCol: TFDDatSColumn;
iColIndex: Integer;
iLen: LongWord;
pBuff: Pointer;
begin
if FModified then
try
oCol := nil;
iColIndex := -1;
oRow := nil;
if FDataSet.GetFieldColumn(FBuffer, FField.FieldNo, oCol, iColIndex, oRow, True) then begin
if oRow.RowState in [rsInserted, rsModified, rsUnchanged] then
oRow.BeginEdit;
iLen := LongWord(Size);
if (FField is TWideStringField) or (FField is TWideMemoField) then
iLen := iLen div SizeOf(WideChar);
// Written zero-length value -> Write is called, Memory=nil, FWritten=True
// Written NULL value -> Write is not called, Memory=nil, FWritten=False
pBuff := Memory;
if (pBuff = nil) and (iLen = 0) and FWritten then
pBuff := Self;
oRow.SetData(iColIndex, pBuff, iLen);
if FField is TBlobField then
TBlobField(FField).Modified := True;
FDataSet.DataEvent(deFieldChange, NativeInt(FField)); // <== Цепочка вызовов приходящих к TCustomDBImageEh.DataChange
end;
except
FDataSet.InternalHandleException;
end;
inherited Destroy;
end;
1.
Код:
procedure TCustomDBImageEh.DataChange(Sender: TObject);
begin
Picture.Graphic := nil; // <== Уничтожение объекта реализующего интерфейс
FPictureLoaded := False;
if FAutoDisplay then
LoadPicture;
if ControlLabel <> nil then
ControlLabel.UpdateCaption;
end;
procedure TPicture.SetGraphic(Value: TGraphic);
var
NewGraphic: TGraphic;
begin
NewGraphic := nil;
if Value <> nil then
begin
NewGraphic := TGraphicClass(Value.ClassType).Create;
NewGraphic.Assign(Value);
NewGraphic.OnChange := Changed;
NewGraphic.OnProgress := Progress;
end;
try
FGraphic.Free; // <== Уничтожение объекта реализующего интерфейс
FGraphic := NewGraphic;
Changed(Self);
except
NewGraphic.Free;
raise;
end;
end;
После возвращения в 12 пункт (TBlobField.LoadFromStreamPersist) при выходе будет AV, так как объект реализующий IStreamPersist уничтожен
Код:
function _IntfClear(var Dest: IInterface): Pointer;
{$IFDEF PUREPASCAL}
var
P: Pointer;
begin
Result := @Dest;
if Dest <> nil then
begin
P := Pointer(Dest);
Pointer(Dest) := nil;
IInterface(P)._Release;
end;
end;
{$ELSE !PUREPASCAL}
{$IFDEF CPUX86}
asm
MOV EDX,[EAX]
TEST EDX,EDX
JE @@1
MOV DWORD PTR [EAX],0
{$IFDEF ALIGN_STACK}
SUB ESP, 4
{$ENDIF ALIGN_STACK}
PUSH EAX
PUSH EDX
MOV EAX,[EDX]
CALL DWORD PTR [EAX] + VMTOFFSET IInterface._Release // <== По EAX лежит мусор $80808080
POP EAX
{$IFDEF ALIGN_STACK}
ADD ESP, 4
{$ENDIF ALIGN_STACK}
@@1:
end;
{$ENDIF CPUX86}
{$ENDIF !PUREPASCAL}