2010-12-13 30 views
18

Khi bất kỳ hậu duệ TGraphic nào đăng ký định dạng tệp đồ họa của riêng nó với một thủ tục lớp TPicture.RegisterFileFormat(), chúng đều được lưu trữ trong biến toàn cục Graphics.FileFormats.Làm thế nào để có được tất cả các định dạng tệp được hỗ trợ từ đơn vị Đồ họa?

Rất tệ khi biến FileFormats không nằm trong phần "giao diện" của "Graphics.pas", vì vậy tôi không thể truy cập nó. Tôi cần phải đọc biến này để thực hiện một bộ lọc đặc biệt cho điều khiển danh sách tập tin của tôi.

Tôi có thể lấy danh sách đó mà không cần sửa mã nguồn của Graphics.pas không?

+2

Ngoài ra còn có liên quan [QC báo cáo # 11837] (http: // qc.embarcadero.com/wc/qcmain.aspx?d=11837) đáng để bỏ phiếu –

Trả lời

20

Bạn đang làm việc với một điều khiển file-list, và có lẽ vì thế một danh sách các tên tập tin. Nếu bạn không cần biết các loại lớp thực tế đã đăng ký, chỉ cho dù một phần mở rộng tệp đã được đăng ký hay không (chẳng hạn như để kiểm tra xem cuộc gọi sau có đến số TPicture.LoadFromFile() có khả năng thành công) hay không, bạn có thể sử dụng công khai GraphicFileMask() để có danh sách các đuôi tệp đã đăng ký và sau đó so sánh tên tệp của bạn với danh sách đó.Ví dụ:

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; 

Hoặc, bạn chỉ có thể tải các tập tin và xem những gì sẽ xảy ra:

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; 

Cập nhật: nếu bạn muốn trích xuất các phần mở rộng và mô tả, bạn có thể sử dụng TStringList.DelimitedText để phân tích kết quả của GraphicFilter() chức năng:

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; 

cập nhật 2: nếu bạn chỉ quan tâm đến một danh sách các phần mở rộng tập tin đăng ký đồ họa, sau đó, giả sử List là đã tạo TStrings hậu duệ, sử dụng này:

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

Bạn có lẽ nên nói ở đây, cũng như nhận xét của bạn về @Cosmin, rằng 'GraphicFilter' có thể được phân tích cú pháp để có được mô tả cũng như mặt nạ. –

+1

@DavidHeffernan: đã hoàn tất. –

+2

+1 bởi vì nó không "hacky" –

11

Dự án GlScene có đơn vị PictureRegisteredFormats.pas thực hiện hack cho điều đó.

+0

+1, đơn vị này hoạt động khá tốt. – RRUZ

+0

Tuyệt vời! Cảm ơn rất nhiều, Uwe. Làm thế nào để bạn nghĩ rằng, nó sẽ là chính xác nếu tôi sẽ xuất bản các giải pháp của GIScene ở đây cho cộng đồng? Đó là mã nguồn mở anyway – Andrew

+0

Lý do tôi không đăng nó ở đây một mình là tôi không muốn suy nghĩ chính xác câu hỏi đó ... –

9

Đây là một bản hack thay thế có thể là an toàn hơn rồi giải pháp GLScene. Nó vẫn là một hack, bởi vì cấu trúc mong muốn là toàn cầu nhưng trong phần thực hiện của đơn vị Graphics.pas, nhưng phương pháp của tôi sử dụng ít "hằng số maigc" hơn (mã hóa mã hóa cứng) và sử dụng hai phương pháp khác nhau để phát hiện chức năng GetFileFormats trong Graphics.pas.

Mã của tôi khai thác thực tế là cả hai TPicture.RegisterFileFormatTPicture.RegisterFileFormatRes cần gọi hàm Graphics.GetFileFormats ngay lập tức. Mã phát hiện mã opcode tương đối bù trừ CALL và đăng ký địa chỉ đích cho cả hai. Chỉ di chuyển về phía trước nếu cả hai kết quả đều giống nhau và điều này bổ sung thêm yếu tố an toàn. Các yếu tố an toàn khác là phương pháp phát hiện chính nó: ngay cả khi phần mở đầu được tạo ra bởi trình biên dịch sẽ thay đổi, miễn là hàm đầu tiên được gọi là GetFileFormats, mã này tìm thấy nó.

Tôi sẽ không đặt "Warning: This will crash when Graphics.pas is compiled with the 'Use Debug DCUs' option." ở đầu đơn vị (như được tìm thấy trong mã GLScene), bởi vì tôi đã thử nghiệm với cả debug dcu và không debug dcu và nó hoạt động. Cũng được thử nghiệm với các gói và nó vẫn hoạt động.

Mã này chỉ hoạt động cho mục tiêu 32 bit, do đó việc sử dụng rộng rãi Integer cho hoạt động của con trỏ. Tôi sẽ cố gắng làm cho công việc này cho các mục tiêu 64bit ngay sau khi tôi sẽ nhận được trình biên dịch Delphi XE2 của tôi được cài đặt.

Cập nhật: Một phiên bản hỗ trợ 64 bit có thể được tìm thấy ở đây: 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. 
+0

Tôi có phiên bản hoạt động với 64 bit. Bạn có muốn tôi ghi đè nó cho bạn không? –

+7

Hàm 'GetListOfRegisteredPictureFileFormats()' có thể được thực hiện khác nhau bằng cách sử dụng 'TStringList.DelimitedText' để phân tích kết quả của công chúng [' Graphics.GraphicFilter() '] (http://docwiki.embarcadero.com/Libraries/XE2/ vi/Vcl.Graphics.GraphicFilter). Đây là hàm tương tự mà 'TOpenPictureDialog' sử dụng để tạo ra' Bộ lọc' của nó. Không cần hack cấp thấp. Một bản hack cấp thấp sẽ chỉ cần thiết khi truy cập vào trường 'TFileFormat.GraphicClass', các mô tả và phần mở rộng đã đăng ký có thể truy cập công khai, không phải là không thể chuyển tiếp. –

+1

Vâng, cả hai giải pháp mới đều được chấp nhận. Tôi đã bỏ phiếu cho cả hai) Tôi đã bỏ chọn câu trả lời của Uwe cho đến khi hết thời gian hết hạn. – Andrew

Các vấn đề liên quan