Как получить все зарегистрированные форматы файлов из VCL.Graphics, но 64-битные

В моем 32-битном приложении я использую модуль FindRegisteredPictureFileFormats, предоставленный Cosmin Prund => (Как получить все поддерживаемые форматы файлов из графического блока?).

Мне нужно то же самое, но для 64 бит. Дэвид Хеффернан ответил, что у него уже есть 64-битная версия. Можно ли обнародовать этот код?

Большое спасибо !!


person Frédéric SCHENCKEL    schedule 04.03.2016    source источник
comment
Решение Реми (принятый ответ на связанный вопрос) будет нормально работать в 64-битном коде. Есть ли причина, по которой вы выбрали более хакерский ответ, опубликованный Космином, а не Реми?   -  person Ken White    schedule 04.03.2016
comment
Поскольку у меня есть графика в потоке, мне нужен графический класс, чтобы загрузить ее правильно. Я не могу использовать трюк, загрузив файл и увидев, какой графический класс был использован... Я знаю, что FindRegisteredPictureFileFormats - это хак, но он отлично работает в 32-битной версии. Но если я смогу получить результат другим способом, я, конечно, смогу адаптировать свой код!   -  person Frédéric SCHENCKEL    schedule 05.03.2016
comment
Вы можете изменить код Remy для загрузки из потока, а не из файла, и использовать тот же метод, не прибегая к ассемблерному коду, который не является кросс-платформенным (включая 32/64-разрядную версию Windows).   -  person Ken White    schedule 05.03.2016
comment
Откуда у тебя стрим? База данных, Ресурс, ...?   -  person Sir Rufo    schedule 05.03.2016
comment
Вы не знаете, какие форматы поддерживаются? Или вы пишете код, который подключается к хост-приложению?   -  person David Heffernan    schedule 05.03.2016
comment
Вау, здесь много шумовых комментариев. Ответ Реми, похоже, решает проблему XY, указанную в удаленном комментарии.   -  person Free Consulting    schedule 05.03.2016
comment
Да, приложение состоит из плагинов, поэтому доступные форматы изображений могут различаться в зависимости от приложения, создавшего поток, и от того, которое его использует. Мне нужно выбрать доступный на платформе чтения, а не на платформе записи. Конечно, я мог бы изменить это поведение, но это означает, что я немного потеряю в гибкости, а платформа Win32 ведет себя иначе, чем Win64.   -  person Frédéric SCHENCKEL    schedule 05.03.2016
comment
@Ken, насколько я знаю, это LoadFromFile, который проверяет правильный класс, LoadFromStream нужен непосредственно правильный класс, или я ошибаюсь? Конечно, кроссплатформенная совместимость тоже не помешала бы!!!   -  person Frédéric SCHENCKEL    schedule 05.03.2016
comment
Изменение модуля Graphics.pas также не самое интересное решение, поскольку я использую пакеты, было бы несколько сложно заставить все пакеты полагаться на мой модифицированный Graphics.pas.   -  person Frédéric SCHENCKEL    schedule 05.03.2016


Ответы (1)


Я считаю, что этот блок делает то, что вы ищете. Я тестировал его на 32-битной и 64-битной Windows, с пакетами времени выполнения и без них. Я не тестировал его с распределением памяти сверху вниз, но я не верю, что есть ошибки усечения указателя.

unit FindRegisteredPictureFileFormats;

{$POINTERMATH ON}

interface

uses Classes, Contnrs;

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

// This returns the list of TGraphicClass registered; True for successful hack, false
// for unsuccesful hach
function GetListOfRegisteredPictureTypes(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: Cardinal;
  end;

  PLongAbsoluteJumpOpcode = ^TLongAbsoluteJumpOpcode;

  TReturnTList = function: TList;

  // Structure copied from Graphics unit.
  PFileFormat = ^TFileFormat;

  TFileFormat = record
    GraphicClass: TGraphicClass;
    Extension: string;
    Description: string;
    DescResID: Integer;
  end;

function FindFirstRelativeCallOpcode(StartOffset: NativeUInt): NativeUInt;
var
  Ram: ^Byte;
  i: Integer;
  PLongJump: PLongAbsoluteJumpOpcode;
begin
  Ram := nil;

  PLongJump := PLongAbsoluteJumpOpcode(@Ram[StartOffset]);
  if (PLongJump^.OpCode[0] = $FF) and (PLongJump^.OpCode[1] = $25) then
{$IF Defined(WIN32)}
    Result := FindFirstRelativeCallOpcode(PNativeUInt(PLongJump^.Destination)^)
{$ELSEIF Defined(Win64)}
    Result := FindFirstRelativeCallOpcode(PNativeUInt(PLongJump^.Destination + StartOffset + SizeOf(PLongJump^))^)
{$ELSE}
    {$MESSAGE Fatal 'Architecture not supported'}
{$ENDIF}
  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: NativeUInt;
  Offset_from_RegisterFileFormatRes: NativeUInt;
begin
  Offset_from_RegisterFileFormat := FindFirstRelativeCallOpcode(NativeUInt(@TPicture.RegisterFileFormat));
  Offset_from_RegisterFileFormatRes := FindFirstRelativeCallOpcode(NativeUInt(@TPicture.RegisterFileFormatRes));

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

function GetListOfRegisteredPictureFileFormats(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(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.
person David Heffernan    schedule 05.03.2016
comment
Большое спасибо, это работает! Примечание. Проверка переполнения и отладка Dcu должны быть деактивированы. При активированном отладочном Dcus тест Offset_from_RegisterFileFormat = Offset_from_RegisterFileFormatRes становится ложным. - person Frédéric SCHENCKEL; 07.03.2016