Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > Графика и игры
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 12.03.2017, 15:03
tsa tsa вне форума
Прохожий
 
Регистрация: 12.03.2017
Сообщения: 3
Версия Delphi: Delphi 2010
Репутация: 10
Вопрос Чтение данных Exif файла jpg

Прошу помочь с решением следующей проблемы. Я написал функцию FileSetDateExif, устанавливающую время модификации файла JPG равным времени его создания 'CreateDate', прописанному в Exif. Функция необходима для сведения в один временной ряд уже модифицированных файлов JPG от разных фотоаппаратов.
Выполнение данной функции сопровождается возникновением исключения "Project … raised exception class EExternalException with message ‘External exception c0000002’" при установке времени каждого файла.
Проблему частично решил обрамлением вызова функции конструкцией "try ... except" и установкой опции игнорирования данного сообщения. Однако видимо есть еще иные побочные эффекты упомянутого выше исключения, так как после вызова написанной функции при работе встроенного мною в оболочку моего программного комплекса медиаплеера стали возникать невиданные ранее прерывания от access violation до полного вылета программы.
Работу функции исследовал под Windows 7 x64, Delphi 2010. Путем последовательного комментирования операторов установил, что возникновение исключения при выполнении оператора SetFileTime(hndl,nil,nil,@aFileTime) происходит если ранее выполнялся оператор WicDecoder.GetFrame(0,FrameDecode).
Буду благодарен за любой совет, так как собственная фантазия уже исчерпалась. Также буду благодарен за любое прояснение ситуации по характеристике возникающего исключения.

Ответ на вопрос дан lmikle : "вызывать _release напрямую не рекомендуется... достаточно просто ... присвоить Nil".
Приведенный ниже код исправлен в соответствии с данной рекомендацией и теперь работает безукоризненно.


Код:
unit uHDDCam;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, MMSystem, Math, Mask, ExtCtrls, ComCtrls, Buttons, uChildAis,
  ToolWin, DateUtils, wincodec, ComObj, ValEdit, ActiveX;
.........................................................

var
  fmHDDCam: TfmHDDCam;
  function TzSpecificLocalTimeToSystemTime(lpTimeZoneInformation: PTimeZoneInformation;
   var lpLocalTime, lpUniversalTime: TSystemTime): BOOL; stdcall;
  function SystemTimeToTzSpecificLocalTime(lpTimeZoneInformation: PTimeZoneInformation;
   var lpUniversalTime,lpLocalTime: TSystemTime): BOOL; stdcall;

implementation

uses ....................................................;

  function TzSpecificLocalTimeToSystemTime; external kernel32 name 'TzSpecificLocalTimeToSystemTime';
  function SystemTimeToTzSpecificLocalTime; external kernel32 name 'SystemTimeToTzSpecificLocalTime';

{$R *.dfm}

function FileSetDateExif : Integer;
  var
    WicFactory                    : IWICImagingFactory;
    WicDecoder                    : IWICBitmapDecoder;
    FrameDecode                   : IWICBitmapFrameDecode;
    mdReader                      : IWICMetadataQueryReader;
    Prop                          : TPropVariant;
    hndl                          : THandle;
    NewTime                       : TDateTime;
    tzi                           : TTimeZoneInformation;
    LocalTime,UniversalTime       : TSystemTime;
    aFileTime                     : TFileTime;
begin
  Result:=0;
  hndl:=CreateFile(PChar(LastSelectImageName),GENERIC_READ or GENERIC_WRITE,
   0,nil,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,0);
  if hndl=THandle(-1) then Result:=GetLastError {INVALID_HANDLE_VALUE}
  else
  begin
    try
      WicFactory:=CreateComObject(CLSID_WICImagingFactory) as IWICImagingFactory;
      WicFactory.CreateDecoderFromFileHandle(hndl,TGUID(nil^),WICDecodeMetadataCacheOnDemand,WicDecoder);
      WicFactory:=nil;
      {выполнение следующего оператора приводит к ошибке при записи времени файла!!!}
      WicDecoder.GetFrame(0,FrameDecode);
      WicDecoder:=nil;
      if FrameDecode.GetMetadataQueryReader(mdReader)<>S_OK then
      begin
        ShowMessage('Не могу прочесть Exif '+LastSelectImageName);
        System.Exit;
      end;
      FrameDecode:=nil;
      zeroMemory(@Prop,SizeOf(Prop));
      mdReader.GetMetadataByName('/xmp/CreateDate',prop);
      mdReader:=nil;
      s:=String(Prop.pwszVal);
      s:=BackDateToRight(s);
      NewTime:=StrToDateTime(s);
      GetTimeZoneInformation(tzi);
      DateTimeToSystemTime(NewTime,LocalTime);
      {локальное время файла пересчитывается с учетом часового пояса}
      TzSpecificLocalTimeToSystemTime(@tzi,LocalTime,UniversalTime);
      SystemTimeToFileTime(UniversalTime,aFileTime);
      if SetFileTime(hndl,nil,nil,@aFileTime) then System.Exit;
      Result:=GetLastError;
    finally
      FileClose(hndl);
    end; {finally}
  end; {hndl<>THandle(-1)}
end; {FileSetDateExif}

Последний раз редактировалось tsa, 13.03.2017 в 02:34.
Ответить с цитированием
  #2  
Старый 12.03.2017, 22:04
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,057
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Возможно, библиотека блокирует файл, да так, что даже api функция не может с ним работать. Варианты:
1. Закрывать файл сразу после того, как считал данные, и убивать COM-объект, а только потом уже ставить время.
2. Найти другую библиотеку чтения EXIF. Видел где-то в исходниках такую. Там полностью Delphi-код, никаких сторонних объектов не используется.
Ответить с цитированием
  #3  
Старый 12.03.2017, 23:24
tsa tsa вне форума
Прохожий
 
Регистрация: 12.03.2017
Сообщения: 3
Версия Delphi: Delphi 2010
Репутация: 10
По умолчанию

Цитата:
Сообщение от lmikle
Возможно, библиотека блокирует файл, да так, что даже api функция не может с ним работать. Варианты:
1. Закрывать файл сразу после того, как считал данные, и убивать COM-объект, а только потом уже ставить время.
Это я проверил сразу, прощу прощения, что не упомянул об этом в старт-посте. После получения времени я закрыл файл и заново получил его Handle. Но результат работы от этого не изменился. А все объекты у меня убиваются сразу по мере возможности. Еще я менял параметры создания объектов и получения Handle файла, но результат все тот же. Исходя из последующего возникновения access violation в плеере моего MDI приложения предполагаю, что портится память. Вот только что решил попробовать "освежить" ее и после WicDecoder._Release; поставил
WicFactory.CreateDecoderFromFileHandle(hndl,TGUID( nil^),WICDecodeMetadataCacheOnDemand,WicDecoder);
WicDecoder._Release;
WicFactory._Release;

с надеждой, что испорченное место "перепашется" заново как надо, но получил access violation на операторе WicDecoder._Release; хотя декодер только создался предыдущим оператором и не мог быть запорчен.

Цитата:
Сообщение от lmikle
2. Найти другую библиотеку чтения EXIF. Видел где-то в исходниках такую. Там полностью Delphi-код, никаких сторонних объектов не используется.
Буду крайне благодарен, если Вам удастся вспомнить, что это была за библиотека.

Последний раз редактировалось tsa, 12.03.2017 в 23:29.
Ответить с цитированием
  #4  
Старый 13.03.2017, 00:37
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,057
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

ну, например, вот есть:
https://delphihaven.wordpress.com/ccr-exif/

Да, кстати, вызывать _release напрямую не рекомендуется. Иначе объект может "помереть" внезапно. Если уж так надо заставить COM-объект "испариться", то достаточно просто переменно присвоить Nil.
Ответить с цитированием
Этот пользователь сказал Спасибо lmikle за это полезное сообщение:
tsa (13.03.2017)
  #5  
Старый 13.03.2017, 02:24
tsa tsa вне форума
Прохожий
 
Регистрация: 12.03.2017
Сообщения: 3
Версия Delphi: Delphi 2010
Репутация: 10
По умолчанию

Цитата:
Сообщение от lmikle
ну, например, вот есть:
https://delphihaven.wordpress.com/ccr-exif/
Спасибо. Скачал.

Цитата:
Сообщение от lmikle
Да, кстати, вызывать _release напрямую не рекомендуется. Иначе объект может "помереть" внезапно. Если уж так надо заставить COM-объект "испариться", то достаточно просто переменно присвоить Nil.
Боюсь сглазить, но кажется в этом и было все дело. Исправил и все заработало как надо. И главное ж я на эту черточку впереди внимание обратил, но за неимением более подходящего метода, скрепя сердце, использовал, а возможность присвоения nil как-то совсем вылетела из головы. А потом уже про правильность освобождения объектов даже и мысли не возникало, - грешил на чужое, хотя напортачил сам. Большое Вам спасибо!

Последний раз редактировалось tsa, 13.03.2017 в 02:37.
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 10:34.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2023

ВКонтакте   Facebook   Twitter