Подтвердить что ты не робот

Как получить все поддерживаемые форматы файлов из графического блока?

Когда любой потомок TGraphic регистрирует собственный формат графического файла с процедурой класса TPicture.RegisterFileFormat(), они все хранятся в глобальной переменной Graphics.FileFormats.

Слишком плохо, что переменная FileFormats отсутствует в разделе "interface" в разделе "Graphics.pas", поэтому я не могу получить к ней доступ. Мне нужно прочитать эту переменную, чтобы реализовать специальный фильтр для моего элемента управления списком файлов.

Можно ли получить этот список без ручной установки исходного кода Graphics.pas?

4b9b3361

Ответ 1

Вы работаете с элементом управления списком файлов и, предположительно, таким образом, списком имен файлов. Если вам не нужно знать фактические типы типов TGraphic, которые зарегистрированы, только если зарегистрировано заданное расширение файла (например, чтобы проверить, может ли более поздний вызов TPicture.LoadFromFile() с успехом преуспеть), вы можете используйте общедоступную функцию GraphicFileMask(), чтобы получить список зарегистрированных расширений файлов, а затем сравните свои имена файлов с этим списком. Например:

uses
  SysUtils, Classes, Graphics, Masks;

function IsGraphicClassRegistered(const FileName: String): Boolean;
var
  Ext: String;
  List: TStringList;
  I: Integer;
begin
  Result := False;
  Ext := ExtractFileExt(FileName);
  List := TStringList.Create;
  try
    List.Delimiter := ';';
    List.StrictDelimiter := True;
    List.DelimitedText := GraphicFileMask(TGraphic);
    for I := 0 to List.Count-1 do
    begin
      if MatchesMask(FileName, List[I]) then
      begin
        Result := True;
        Exit;
      end;
    end;
  finally
    List.Free;
  end;
end;

Или вы можете просто загрузить файл и посмотреть, что произойдет:

uses
  Graphics;

function GetRegisteredGraphicClass(const FileName: String): TGraphicClass;
var
  Picture: TPicture;
begin
  Result := nil;
  try
    Picture := TPicture.Create;
    try
      Picture.LoadFromFile(FileName);
      Result := TGraphicClass(Picture.Graphic.ClassType);
    finally
      Picture.Free;
    end;
  except
  end;
end;

Обновление:, если вы хотите извлечь расширения и описания, вы можете использовать TStringList.DelimitedText для анализа результата функции GraphicFilter():

uses
  SysUtils, Classes, Graphics;

function RPos(const ASub, AIn: String; AStart: Integer = -1): Integer;
var
  i: Integer;
  LStartPos: Integer;
  LTokenLen: Integer;
begin
  Result := 0;
  LTokenLen := Length(ASub);
  // Get starting position
  if AStart < 0 then begin
    AStart := Length(AIn);
  end;
  if AStart < (Length(AIn) - LTokenLen + 1) then begin
    LStartPos := AStart;
  end else begin
    LStartPos := (Length(AIn) - LTokenLen + 1);
  end;
  // Search for the string
  for i := LStartPos downto 1 do begin
    if Copy(AIn, i, LTokenLen) = ASub then begin
      Result := i;
      Break;
    end;
  end;
end;

procedure GetRegisteredGraphicFormats(AFormats: TStrings);
var
  List: TStringList;
  i, j: Integer;
  desc, ext: string;
begin
  List := TStringList.Create;
  try
    List.Delimiter := '|';
    List.StrictDelimiter := True;
    List.DelimitedText := GraphicFilter(TGraphic);
    i := 0;
    if List.Count > 2 then
      Inc(i, 2); // skip the "All" filter ...
    while i <= List.Count-1 do
    begin
      desc := List[i];
      ext := List[i+1];
      j := RPos('(', desc);
      if j > 0 then
        desc := TrimRight(Copy(desc, 1, j-1)); // remove extension mask from description
      AFormats.Add(ext + '=' + desc);
      Inc(i, 2);
    end;
  finally
    List.Free;
  end;
end;

Обновить 2:, если вас просто интересует список зарегистрированных расширений графических файлов, тогда, предполагая, что List является уже созданным потомком TStrings, используйте это:

ExtractStrings([';'], ['*', '.'], PChar(GraphicFileMask(TGraphic)), List);

Ответ 2

Проект GlScene имеет блок PictureRegisteredFormats.pas, который реализует для этого взлом.

Ответ 3

Здесь альтернативный взломан, который может быть безопаснее, затем GLScene. Это все еще хак, потому что желаемая структура является глобальной, но в разделе реализации блока Graphics.pas, но мой метод использует намного меньше "констант maigc" (жестко закодированные смещения в коде) и использует два разных метода для обнаружения функции GetFileFormats в Graphics.pas.

Мой код использует тот факт, что как TPicture.RegisterFileFormat, так и TPicture.RegisterFileFormatRes необходимо немедленно вызвать функцию Graphics.GetFileFormats. Код обнаруживает код операции относительного смещения CALL и регистрирует адрес назначения для обоих. Только продвигается вперед, если оба результата одинаковы, и это добавляет коэффициент безопасности. Другим фактором безопасности является сам метод обнаружения: даже если пролог, сгенерированный компилятором, изменится, если первая функция называется GetFileFormats, этот код находит это.

Я не собираюсь помещать "Warning: This will crash when Graphics.pas is compiled with the 'Use Debug DCUs' option." в верхнюю часть устройства (как показано в коде GLScene), потому что я тестировал как с debug dcu, так и без отладки dcu, и это сработало. Также тестировался с пакетами, и он все еще работал.

Этот код работает только для 32-битных целей, поэтому широкое использование Integer для операций указателя. Я попытаюсь сделать эту работу для 64-битных целей, как только я установлю свой компилятор Delphi XE2.

Обновление: Версия, поддерживающая 64 бит, можно найти здесь: fooobar.com/info/284037/...

unit FindReigsteredPictureFileFormats;

interface

uses Classes, Contnrs;

// Extracts the file extension + the description; Returns True if the hack was successful,
// False if unsuccesful.
function GetListOfRegisteredPictureFileFormats(const List: TStrings): Boolean;

// This returns the list of TGraphicClass registered; True for successful hack, false
// for unsuccesful hach
function GetListOfRegisteredPictureTypes(const List:TClassList): Boolean;

implementation

uses Graphics;

type
  TRelativeCallOpcode = packed record
    OpCode: Byte;
    Offset: Integer;
  end;
  PRelativeCallOpcode = ^TRelativeCallOpcode;

  TLongAbsoluteJumpOpcode = packed record
    OpCode: array[0..1] of Byte;
    Destination: PInteger;
  end;
  PLongAbsoluteJumpOpcode = ^TLongAbsoluteJumpOpcode;

  TMaxByteArray = array[0..System.MaxInt-1] of Byte;
  PMaxByteArray = ^TMaxByteArray;

  TReturnTList = function: TList;

  // Structure copied from Graphics unit.
  PFileFormat = ^TFileFormat;
  TFileFormat = record
    GraphicClass: TGraphicClass;
    Extension: string;
    Description: string;
    DescResID: Integer;
  end;

function FindFirstRelativeCallOpcode(const StartOffset:Integer): Integer;
var Ram: PMaxByteArray;
    i: Integer;
    PLongJump: PLongAbsoluteJumpOpcode;
begin
  Ram := nil;

  PLongJump := PLongAbsoluteJumpOpcode(@Ram[StartOffset]);
  if (PLongJump^.OpCode[0] = $FF) and (PLongJump^.OpCode[1] = $25) then
    Result := FindFirstRelativeCallOpcode(PLongJump^.Destination^)
  else
    begin
      for i:=0 to 64 do
        if PRelativeCallOpcode(@Ram[StartOffset+i])^.OpCode = $E8 then
          Exit(StartOffset + i + PRelativeCallOpcode(@Ram[StartOffset+i])^.Offset + 5);
      Result := 0;
    end;
end;

procedure FindGetFileFormatsFunc(out ProcAddr: TReturnTList);
var Offset_from_RegisterFileFormat: Integer;
    Offset_from_RegisterFileFormatRes: Integer;
begin
  Offset_from_RegisterFileFormat := FindFirstRelativeCallOpcode(Integer(@TPicture.RegisterFileFormat));
  Offset_from_RegisterFileFormatRes := FindFirstRelativeCallOpcode(Integer(@TPicture.RegisterFileFormatRes));

  if (Offset_from_RegisterFileFormat = Offset_from_RegisterFileFormatRes) then
    ProcAddr := TReturnTList(Pointer(Offset_from_RegisterFileFormat))
  else
    ProcAddr := nil;
end;

function GetListOfRegisteredPictureFileFormats(const List: TStrings): Boolean;
var GetListProc:TReturnTList;
    L: TList;
    i: Integer;
begin
  FindGetFileFormatsFunc(GetListProc);
  if Assigned(GetListProc) then
    begin
      Result := True;
      L := GetListProc;
      for i:=0 to L.Count-1 do
        List.Add(PFileFormat(L[i])^.Extension + '=' + PFileFormat(L[i])^.Description);
    end
  else
    Result := False;
end;

function GetListOfRegisteredPictureTypes(const List:TClassList): Boolean;
var GetListProc:TReturnTList;
    L: TList;
    i: Integer;
begin
  FindGetFileFormatsFunc(GetListProc);
  if Assigned(GetListProc) then
    begin
      Result := True;
      L := GetListProc;
      for i:=0 to L.Count-1 do
        List.Add(PFileFormat(L[i])^.GraphicClass);
    end
  else
    Result := False;
end;

end.