Как получить все поддерживаемые форматы файлов из графического блока? - PullRequest
18 голосов
/ 13 декабря 2010

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

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

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

Ответы [ 3 ]

20 голосов
/ 04 февраля 2013

Вы работаете с элементом управления списком файлов и, следовательно, списком имен файлов.Если вам не нужно знать фактические типы классов TGraphic, которые зарегистрированы, только независимо от того, зарегистрировано данное расширение файла или нет (например, чтобы проверить, может ли последующий вызов к TPicture.LoadFromFile() быть успешным), вы можетеиспользуйте функцию public 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);
11 голосов
/ 13 декабря 2010

В проекте GlScene есть блок PictureRegisteredFormats.pas , который реализует для этого хак.

9 голосов
/ 04 февраля 2013

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

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

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

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

Обновление: Здесь можно найти версию с поддержкой 64-битной версии: https://stackoverflow.com/a/35817804/505088

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