Components for Delphi and C++ Builder.

Перейти в английский форум
Перейти на EhLib.com
Текущее время: 19 апр 2024, 00:41

Часовой пояс: UTC




Начать новую тему Ответить на тему  [ Сообщений: 4 ] 
Автор Сообщение
 Заголовок сообщения: AV в TDBImageEh + FireDAC
СообщениеДобавлено: 02 мар 2020, 10:20 
Не в сети

Зарегистрирован: 16 фев 2018, 04:13
Сообщений: 54
Откуда: АО ОТЭКО
При загрузке картинки с помощью контекстного меню (пункт Из файла...), возникает 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
  1. DBCtrlsEh.TCustomDBImageEh.DataChange(???)
  2. DBCtrlsEh.TFieldDataLinkEh.RecordChanged(???)
  3. Data.DB.TDataLink.DataEvent(???,???)
  4. DBCtrlsEh.TFieldDataLinkEh.DataEvent(deFieldChange,2120935728)
  5. Data.DB.TDataSource.NotifyLinkTypes(deFieldChange,2120935728,True)
  6. Data.DB.TDataSource.NotifyDataLinks(deFieldChange,2120935728)
  7. Data.DB.TDataSource.DataEvent(deFieldChange,2120935728)
  8. Data.DB.TDataSet.DataEvent(deFieldChange,2120935728)
  9. FireDAC.Comp.DataSet.TFDDataSet.DataEvent(deFieldChange,2120935728)
  10. FireDAC.Comp.DataSet.TFDBlobStream.Destroy
  11. System.TObject.Free
  12. Data.DB.TBlobField.LoadFromStreamPersist(TJPEGImage($7E3D61E4) as IStreamPersist)
  13. Data.DB.TBlobField.Assign(???)
  14. DBCtrlsEh.TCustomDBImageEh.UpdateData(???)
  15. DBCtrlsEh.TCustomDBImageEh.MenuItemLoad(???)
  16. 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}


Вложения:
ScreenShot.png
ScreenShot.png [ 136.13 KiB | Просмотров: 821 ]
Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: AV в TDBImageEh + FireDAC
СообщениеДобавлено: 02 мар 2020, 14:06 
Не в сети

Зарегистрирован: 21 дек 2011, 18:48
Сообщений: 1549
Добрый день.

На тестовом проекте ошибка не воспроизводится.

В указанной точке останова при отладке, у нас в
function _IntfClear(var Dest: IInterface): Pointer;
переменная Dest = nil и строка
CALL DWORD PTR [EAX] + VMTOFFSET IInterface._Release
не вызывается.

Попробуйте создать и прислать простой Демо проект в котором бы ошибка воспроизводилась.
Проверьте разные типы графических файлов. Возможно ошибка в этом.

_________________
Best regards
EhLib support Team


Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: AV в TDBImageEh + FireDAC
СообщениеДобавлено: 03 мар 2020, 07:50 
Не в сети

Зарегистрирован: 16 фев 2018, 04:13
Сообщений: 54
Откуда: АО ОТЭКО
Не могу стабильно повторить со встроенным менеджером памяти, вот воспроизводится с FastMM4.
Это он тут же забивает паттерном $80808080 освобождённую память, чтобы отлавливать обращения к освобождённым объектам.
Со встроенным менеджером сложно попасть в момент между освобождением объекта и освобождением интерфейса.


Вложения:
EhLibErrTDBImageEh1.zip [217.58 KiB]
Скачиваний: 56
Вернуться наверх
 Профиль  
 
 Заголовок сообщения: Re: AV в TDBImageEh + FireDAC
СообщениеДобавлено: 05 мар 2020, 10:07 
Не в сети

Зарегистрирован: 21 дек 2011, 18:48
Сообщений: 1549
Добрый день.

Ошибку исправим в следующей сборке.
Спасибо за информацию.

_________________
Best regards
EhLib support Team


Вернуться наверх
 Профиль  
 
Показать сообщения за:  Сортировать по:  
Начать новую тему Ответить на тему  [ Сообщений: 4 ] 

Часовой пояс: UTC


Кто сейчас на форуме

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  
cron
Powered by phpBB® Forum Software © phpBB Group
Русская поддержка phpBB