Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2017年11月24日] [更新 2017年12月01日] Delphi サンプルプログラム集
907_リソースのビットマップを取得
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
907_GetResourceBitmap.zip [2,407 KB] 2017年12月01日版 (EXE 同梱)







リソースのビットマップを取得


DLL や EXE ファイル等,リソースとしてビットマップ画像が格納されているファイルから,そのビットマップを取得するサンプルです.
以下の参考リンクに,リソースのアイコンを取得するサンプルがありますが,ヒットマップの場合はリソースを含むファイルを LoadLibraryEx( 関数で読み込み,モジュールとしてのハンドルを取得します.そのハンドルを使用してリソースを取得します.






01_リソースの IDを指定してビットマップを取得


Delphi XE Pro のコンパイル済みパッケージ dclstd150.bpl からリソースのビットマップを取得する例です.このビットマップは,このパッケージに登録されているコンポーネントのアイコン (ビットマップ) となっています.
モジュールとしてのハンドルを取得した後,TBitmap.LoadFromResourceName メソッドでビットマップを取得します.ここではリソース名が TMEMO32 のビットマップを取得します.



[備考]
Delphi ではコンポーネントのアイコン (実際にはビットマップ) を作成して,リソースコンパイラで .dcr というファイルを生成してそこに格納します.コンポーネントの登録先のパッケージをコンパイルすると,そのパッケージが再構築されて,コンポーネントのアイコンとしてのビットマップが,そのコンパイル済みのパッケージにリソースとして保存されます.
コンパイル済みパッケージの拡張子は .bpl です.


図1
ビットマップの取得結果

リスト1
LoadFromResourceName を使用して,リソース名からビットマップを取得
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Image1: TImage;
    Edit1: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit1.Text := 'TMEMO32';
  Image1.AutoSize := True;
end;

//=============================================================================
//  コンパイル済みパッケージファイルから指定名前のビットマップを取得
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LResouceFile : string;
  LdwFlag      : Cardinal;
  LhModule     : THandle;
  LBitmap      : TBitmap;
begin
  Image1.Picture.Assign(nil);
  LResouceFile := 'C:\Program Files (x86)\Embarcadero\RAD Studio\8.0\bin\dclstd150.bpl';

  LdwFlag  := LOAD_LIBRARY_AS_DATAFILE or LOAD_WITH_ALTERED_SEARCH_PATH;
  LhModule := LoadLibraryEx(PChar(LResouceFile), 0, LdwFlag);
  try
   if LhModule <> 0 then begin
      LBitmap := TBitmap.Create;
      try
        LBitmap.LoadFromResourceName(LhModule, Trim(Edit1.Text));
        Image1.Picture.Bitmap.Assign(LBitmap);
      finally
        FreeAndNil(LBitmap);
      end;
    end;
  finally
   FreeLibrary(LhModule);
  end;
end;

end.


下図は imageres.dll 内のリソース ID が 5004 のビットマップを取得する例です.
このリソース ID は数値です.そこで LoadFromResourceID 関数でビットマップを取得しています.
imageres.dll にはシステム的にパスが通っていますからディレクトリ名を省略できます.



図2
imageres.dll 内の 5004 のビットマップ

リスト2
LoadFromResourceID を使用して,リソース名からビットマップを取得
//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit1.Text := '5004';
  Image1.AutoSize := True;
end;

//=============================================================================
//  DLLのリソースからビットマップを取得
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LResouceFile : string;
  LdwFlag      : Cardinal;
  LhModule     : THandle;
  LBitmap      : TBitmap;
begin
  Image1.Picture.Assign(nil);
  LResouceFile := 'imageres.dll';

  LdwFlag  := LOAD_LIBRARY_AS_DATAFILE or LOAD_WITH_ALTERED_SEARCH_PATH;
  LhModule := LoadLibraryEx(PChar(LResouceFile), 0, LdwFlag);
  try
   if LhModule <> 0 then begin
      LBitmap := TBitmap.Create;
      try
        LBitmap.LoadFromResourceID(LhModule, StrToInt(Trim(Edit1.Text)));
        Image1.Picture.Bitmap.Assign(LBitmap);
      finally
        FreeAndNil(LBitmap);
      end;
    end;
  finally
   FreeLibrary(LhModule);
  end;
end;




02_ビットマップリソースの列挙


リソースのビットマップを列挙する例です.実際にはリソースの ID の一覧を表示します.一覧表示したリソース ID を選択すると,そのビットマップをリソースから取得して表示します.

参考リンクのサンプルではマルチアイコンを取得するため,処理コードが長く複雑ですが,ビットマップの場合は単純です.ただし,参考リンクのサンプルと同じように,リソース ID には数値と文字列があることに注意します.
下図は Shell32.dll 内と ResourceHacker.exe 内のビットマップリソースを取得した結果です.
Shell32.dll 以外にも compstui.dll, mmcndmgr.dll, netshell.dll, wmploc.dll, inetcpl.cpl 等にもビットマップ画像が格納されています.




図3
設計時の画面
  • リソース ID 一覧表示用の TListBoxと画像表示用の TImage を配置
図4
Shell32.dll 内のビットマップを取得
  • リソース ID を TList に一覧表示
  • リソース ID は数値と文字列の両方ある
  • クリックするとその ID のビットマップを取得して表示
図5
ResourceHacker.exe 内のビットマップを取得

下図は前項と同じ Delphi XE Pro のコンパイル済みパッケージ dclstd150.bpl からリソースのビットマップを取得した結果です.286 個あります.このファイルのビットマップのリソース ID は null 終端文字列となっています.
その下の図は Delphi 6 Pro の標準コンポーネントのアイコンですが,77 個しかありません.



図6
Delphi XE の dclstd150.bpl ファイル内のビットマップリソースの列挙
  • リソース ID は文字列
  • 286 個ある
図7
Delphi 6 の dclstd60.bpl ファイル内のビットマップリソースの列挙
  • 77 個しかない

リスト3
ビットマップリソースのリソース ID を列挙
LoadFromResourceName または LoadImage を使用して,リソース ID からビットマップを取得
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    ListBox1: TListBox;
    Image1: TImage;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
  private
    { Private 宣言 }
    FResouceFile : string;
  public
    { Public 宣言 }
  end;

function EnumResNameProc(hModule: HWND; lpszType: PChar; lpszName:PChar;
  AIndex: LPARAM): LongBool; stdcall;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit1.Text := 'C:\Program Files (x86)\Embarcadero\RAD Studio\8.0\bin\dclstd150.bpl';
  Image1.AutoSize :=True;
end;

//=============================================================================
//  リソースの列挙
//  ここでは RT_BITMAP とする(つまりビットマップ)
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LhModule : THandle;
  LdwFlag  : Cardinal;
begin
  Image1.Picture.Assign(nil);
  ListBox1.Items.Clear;
  ListBox1.Items.BeginUpdate;

  //リソースファイルを指定してモジュールのハンドルを取得
  FResouceFile := Trim(Edit1.Text);
  LdwFlag      := LOAD_LIBRARY_AS_DATAFILE or LOAD_WITH_ALTERED_SEARCH_PATH;
  LhModule     := LoadLibraryEx(PChar(FResouceFile), 0, LdwFlag);
  try
   if LhModule <> 0 then begin
     //リソースIDを列挙
     EnumResourceNames(LhModule, RT_BITMAP, @EnumResNameProc, -1);
   end;
   Label1.Caption := IntToStr(ListBox1.Items.Count);
  finally
    FreeLibrary(LhModule);
    ListBox1.Items.EndUpdate;
  end;
end;

//=============================================================================
//  ListBox1のOnClickイベント
//  リストボックスのItemをクリックしたらそのリソースを読み出してTImageに表示
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
var
  LdwFlag  : Cardinal;
  LhModule : THandle;
  LItemStr : string;
  LItemInt : Integer;
  LBitmap  : TBitmap;
begin
  Image1.Picture.Assign(nil); 
  if ListBox1.ItemIndex =-1 then Exit;

  //リソースファイルを指定してモジュールのハンドルを取得
  LdwFlag  := LOAD_LIBRARY_AS_DATAFILE or LOAD_WITH_ALTERED_SEARCH_PATH;
  LhModule := LoadLibraryEx(PChar(FResouceFile), 0, LdwFlag);
  try
   if LhModule <> 0 then begin
      LBitmap := TBitmap.Create;
      try
        LItemStr := ListBox1.Items[ListBox1.ItemIndex];
        LItemInt := StrToIntDef(LItemStr, -1);

        //リソースIDが数値の時と文字列の時で処理を分岐
        //文字列が数字だけで構成されていると数値と判定してしまう
        if LItemInt < 0 then begin
          LBitmap.LoadFromResourceName(LhModule, LItemStr);;
        end else begin
          LBitmap.LoadFromResourceID(LhModule, LItemInt);
        end;

        Image1.Picture.Bitmap.Assign(LBitmap);
      finally
        //LBitmap.HandleにHBitmapを代入しているのでDeleteObject(LhBitmap)は不要
        FreeAndNil(LBitmap);
      end;
    end;
  finally
    FreeLibrary(LhModule);
  end;
end;

//-----------------------------------------------------------------------------
//  EnumResourceNamesのコールバック関数
//  リソースの列挙
//-----------------------------------------------------------------------------
function EnumResNameProc(hModule: HWND; lpszType: PChar; lpszName: PChar;
  AIndex: LPARAM): LongBool;
var
  LhBitmap  : HBitmap;
  LResIDStr : string;
begin
  Result := True;

  if AIndex = -1 then begin
    //リソースの取得
    LhBitmap := LoadImage(hModule, MakeIntResource(lpszName), IMAGE_BITMAP, 0, 0, 0);
    try
      if LhBitmap <> 0 then begin

        //リソース名のチェック
        //上位WORD(2バイト)が 0ならIDは数値.それ以外は文字列のポインタ
        if HiWord(Cardinal(lpszName)) = 0 then begin
          LResIDStr := IntToStr(Cardinal(lpszName));
        end else begin
          LResIDStr := lpszName;
        end;

        //リソースのIDをリストに追加
        Form1.ListBox1.Items.Add(LResIDStr);
      end;
    finally
      DeleteObject(LhBitmap);
    end;
  end;
end;

end.




03_.dcr ( コンポーネントのアイコンリソース ) からビットマップ取得


Delphi のコンポーネントのアイコンのリソースファイル .dcr は拡張子は違いまが .res ファイルと構造は同じです..dcr ファイルは Delphi のコンポーネントのアイコン専用のリソースファイルということになります.

.dcr あるいは .res ファイルを LoadLibraryEx 関数で読み込んでもモジュールとしてのハンドルは取得できません..dcr には [リソース ID] [ビットマップヘッダ] [ビットマップ] データの順にリソースとしての情報が格納されていますから,これを順番に取り出していけばビットマップが取得できることになります.

もう 1 つの方法は,.dcr をリソースとした実行可能なモジュールを作成してしまう方法です.
例えば plPrevReg.dcr を dll 内にリソースとして埋め込むには以下のようなプロジェクトを作成してコンパイルします.このコードではリソースの名前を plPrevReg.res としていますが,plPrevReg.dcr でも同じです. リソースの名前を plPrevReg.dcr にした場合は,コンパイルすると plPrevReg.res ファイルが自動的に作成されます.


リスト4
plPevReg.res をリソースとして埋め込む DLL を作成するプロジェクトのコード
library plPrevReg;
uses Windows;
{$R plPrevReg.res}
begin
end.


以下は添付の plPrevReg.dcr をリソースとした DLL を作成するサンプルです.作成した DLL を読み込んでリソースとしてのビットマップを取得します.DLL を作成するコードと,LoadLibraryEx 関数で読み込むファイルがその DLL であること以外は前項のサンプルとほとんど同じです.

当然ですが,このサンプルは Delphi のコマンドラインコンパイラを使用します.したがって,実行環境に Delphi がインストールされていないと正常に動作しません.



[備考]
Delphi が標準で提供しているコンポーネント類には .dcr ファイルはありません.また.パッケージの定義ファイル .dpk もありません.
Delphi が標準で提供しているコンポーネントのアイコン (ビットマップ) は,コンパイル済みパッケージファイル .bpl にリソースとして格納されています.


図8
実行結果

リスト5
dcr ファイル (コンポーネントのアイコンのリソースファイル) からビットマップを取得する例
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    ListBox1: TListBox;
    Image1: TImage;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private 宣言 }
    FdcrDllFile : string;
    function dcrToDllFile(AdcrFilePath: string): string;
  public
    { Public 宣言 }
  end;

function EnumResNameProc(hModule: HWND; lpszType: PChar; lpszName:PChar;
  AIndex: LPARAM): LongBool; stdcall;

var
  Form1: TForm1;

implementation

uses
  ShellAPI,
  StrUtils;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit1.Text := ExpandFileName('.\plPrevReg.dcr');
  Image1.AutoSize :=True;
end;

//=============================================================================
//  フォーム破棄時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
var
  LdprFile : string;
  LresFile : string;
begin
  LdprFile := ChangeFileExt(FdcrDllFile, '.dpr');
  LresFile := ChangeFileExt(FdcrDllFile, '.res');
  DeleteFile(FdcrDllFile);
  DeleteFile(LdprFile);
  DeleteFile(LresFile);
end;

//=============================================================================
//  リソースの列挙
//  ここでは RT_BITMAP とする(つまりビットマップ)
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LdcrFilePath : string;
  LhModule     : THandle;
  LdwFlag      : Cardinal;
begin
  Image1.Picture.Assign(nil);
  if FdcrDllFile <> '' then FormDestroy(nil);

  LdcrFilePath := Trim(Edit1.Text);
  FdcrDllFile  := dcrToDllFile(LdcrFilePath);

  ListBox1.Items.Clear;
  ListBox1.Items.BeginUpdate;

  if FdcrDllFile <> '' then begin
    //DLLファイルを指定してモジュールのハンドルを取得
    LdwFlag     := LOAD_LIBRARY_AS_DATAFILE or LOAD_WITH_ALTERED_SEARCH_PATH;
    LhModule    := LoadLibraryEx(PChar(FdcrDllFile), 0, LdwFlag);
    try
     if LhModule <> 0 then begin
       //リソースIDを列挙
       EnumResourceNames(LhModule, RT_BITMAP, @EnumResNameProc, -1);
     end;
     Label1.Caption := IntToStr(ListBox1.Items.Count);
    finally
      FreeLibrary(LhModule);
    end;
  end;
  ListBox1.Items.EndUpdate;
end;

//=============================================================================
//  ListBox1のOnClickイベント
//  リストボックスのItemをクリックしたらそのリソースを読み出してTImageに表示
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
var
  LdwFlag  : Cardinal;
  LhModule : THandle;
  LItemStr : string;
  LItemInt : Integer;
  LBitmap  : TBitmap;
begin
  Image1.Picture.Assign(nil); 
  if ListBox1.ItemIndex =-1 then Exit;

  //リソースファイルを指定してモジュールのハンドルを取得
  LdwFlag  := LOAD_LIBRARY_AS_DATAFILE or LOAD_WITH_ALTERED_SEARCH_PATH;
  LhModule := LoadLibraryEx(PChar(FdcrDllFile), 0, LdwFlag);
  try
   if LhModule <> 0 then begin
      LBitmap := TBitmap.Create;
      try
        LItemStr := ListBox1.Items[ListBox1.ItemIndex];
        LItemInt := StrToIntDef(LItemStr, -1);

        //リソースIDが数値の時と文字列の時で処理を分岐
        //文字列が数字だけで構成されていると数値と判定してしまう
        if LItemInt < 0 then begin
          LBitmap.LoadFromResourceName(LhModule, LItemStr);;
        end else begin
          LBitmap.LoadFromResourceID(LhModule, LItemInt);
        end;

        Image1.Picture.Bitmap.Assign(LBitmap);
      finally
        //LBitmap.HandleにHBitmapを代入しているのでDeleteObject(LhBitmap)は不要
        FreeAndNil(LBitmap);
      end;
    end;
  finally
    FreeLibrary(LhModule);
  end;
end;

//-----------------------------------------------------------------------------
//  EnumResourceNamesのコールバック関数
//  リソースの列挙
//-----------------------------------------------------------------------------
function EnumResNameProc(hModule: HWND; lpszType: PChar; lpszName: PChar;
  AIndex: LPARAM): LongBool;
var
  LhBitmap  : HBitmap;
  LResIDStr : string;
begin
  Result := True;

  if AIndex = -1 then begin
    //リソースの取得
    LhBitmap := LoadImage(hModule, MakeIntResource(lpszName), IMAGE_BITMAP, 0, 0, 0);
    try
      if LhBitmap <> 0 then begin

        //リソース名のチェック
        //上位WORD(2バイト)が 0ならIDは数値.それ以外は文字列のポインタ
        if HiWord(Cardinal(lpszName)) = 0 then begin
          LResIDStr := IntToStr(Cardinal(lpszName));
        end else begin
          LResIDStr := lpszName;
        end;

        //リソースのIDをリストに追加
        Form1.ListBox1.Items.Add(LResIDStr);
      end;
    finally
      DeleteObject(LhBitmap);
    end;
  end;
end;

//-----------------------------------------------------------------------------
//  拡張子がdcrのファイルからdllファイルを作成する関数
//  実行環境にDelphiがインストールされている必要がある
//
//  AdcrFilePath : 対象のdcrファイルのフルパス
//  戻り値はdllのパス名.失敗すると空文字を返す
//  グローバルな変数は使用していない
//
//  usesに  ShellAPI,StrUtilsが必要
//-----------------------------------------------------------------------------
function TForm1.dcrToDllFile(AdcrFilePath: string): string;
var
  LSelfAppPath   : string;
  LResFilePath   : string;
  LdprFilePath   : string;
  LdprCodeList   : TStringList;
  Lpdcc32ExePath : PChar;
  Ldcc32ExePath  : string;
  LCompilerName  : string;
  LCmdLineStr    : string;
  LStartInfo     : TStartupInfo;
  LProcessInfo   : TProcessInformation;
begin
  Result := '';

  //res,dprのパス名を生成
  LSelfAppPath := ExtractFilePath(Application.ExeName);
  LResFilePath := LSelfAppPath + ChangeFileExt(ExtractFileName(AdcrFilePath), '.res');
  LdprFilePath := LSelfAppPath + ChangeFileExt(ExtractFileName(AdcrFilePath), '.dpr');

  DeleteFile(LResFilePath);
  DeleteFile(LdprFilePath);
  //resファイル保存
  CopyFile(PChar(AdcrFilePath), PChar(LResFilePath), False);

  //DLL作成用のプロジェクトコードを作成して保存
  LdprCodeList := TStringList.Create;
  try
    LdprCodeList.Add('library ' + ChangeFileExt(ExtractFileName(LdprFilePath), '') + ';');
    LdprCodeList.Add('uses Windows;');
    LdprCodeList.Add('{$R '+ ExtractFileName(LResFilePath) + '}');
    LdprCodeList.Add('begin');
    LdprCodeList.Add('end.');
    LdprCodeList.SaveToFile(LdprFilePath);
  finally
    FreeAndNil(LdprCodeList);
  end;

  //コマンドラインコンパイラ(dcc32.exe)のフルパスを取得
  Lpdcc32ExePath := StrAlloc(MAX_PATH + 1);
  try
    if FindExecutable(PChar(LdprFilePath), nil, Lpdcc32ExePath) > 32 then begin
      Ldcc32ExePath := Lpdcc32ExePath;
      if Ldcc32ExePath <> '' then begin
        LCompilerName := ExtractFileName(Ldcc32ExePath);
        Ldcc32ExePath := ReplaceText(Ldcc32ExePath, LCompilerName, 'dcc32.exe');
      end;
    end else begin
      exit;
    end;
  finally
    StrDispose(Lpdcc32ExePath);
  end;
  if LowerCase(ExtractFileName(Ldcc32ExePath)) <> 'dcc32.exe' then exit;

  Ldcc32ExePath := AnsiQuotedStr(Trim(Ldcc32ExePath), '"');
  //作成したプロジェクトファイルをコンパイルしてDLL作成
  GetStartupInfo(LStartInfo);
  LStartInfo.dwXSize := 0;
  LStartInfo.dwYSize := 0;
  LStartInfo.wShowWindow := SW_HIDE;

  LCmdLineStr := Ldcc32ExePath + ' ' + AnsiQuotedStr(LdprFilePath,'"');
  UniqueString(LCmdLineStr);
  if not CreateProcess(nil,
                       PChar(LCmdLineStr),
                       nil,
                       nil,
                       False,
                       0,
                       nil,
                       PChar(LSelfAppPath),
                       LStartInfo,
                       LProcessInfo) then begin
  end else begin
    //コンパイルが終了するまで待つ
    while WaitForSingleObject(LProcessInfo.hProcess, 0) = WAIT_TIMEOUT do begin
      Application.ProcessMessages;
      Sleep(1);
    end;
    CloseHandle(LProcessInfo.hProcess);
    CloseHandle(LProcessInfo.hThread);

    Result := ChangeFileExt(LdprFilePath, '.dll');
  end;
end;

end.




04_unitResFile を利用してリソースファイルのビットマップを取得


コマンドラインコンパイラを使用しないでリソースファイルからリソースを取り出す例です.
unitResFile, unitResourceDetails というリソースファイル操作用のユニット類を利用しています.これらのユニットを使用すると,リソースファイルに格納されている各々のリソース ID の値,リソースの型が取得でき,リソースのデータを個別に TMemoryStream に保存できます.

これらのユニットは,リソースエディタ XN Resource Editor の開発者である Colin Wilson 氏が提供していたユニットですが,2017年11月24日現在,Colin Wilson 氏のサイトは存在しません.今回は以下の SourceForge のページからダウンロードしたファイルを使用しました.
ファイル名をクリックして表示されるページにダウンロードのリンクがあります.そこからダウンロードできます.


ビットマップの場合,リソースから取り出した TMemoryStream のデータの先頭部分にビットマップファイルヘッダの情報がない場合はビットマップとして認識してくれません.ない場合は先頭にファイルヘッダ情報を追加します.Delphi の場合は TBitmapFileHeader レコード型の変数に各メンバの値をセットします.
以下が実際のサンプルです.下図では添付のコンポーネントのリソースファイルからコンポーネントのアイコンのビットマップ画像を取得して表示しています.



[備考 1]
JEDI プロジェクトにも同名のユニットファイルがありますが,Delphi XE で利用するには修正が必要です.JEDI プロジェクトの同名の 2 つのファイルは Ansi 版の Delphi 6 であれば使用可能です.ただし,本サンプルのコードは,Delphi 6 では使用できない部分は変更が必要です.

[備考 2]
コンポーネントアイコン用のリソースファイル .dcr を読み込めないリソースエディタがありますが,拡張子を .res にすれば読み込めるリソースエディタもあります.


図9
コンポーネントのアイコンリソースファイルからビットマップ画像を取得

下図は,添付の DlgSetRes.dres というリソースファイルからビットマップ画像を取得して表示しています.このリソースファイルには JPEG 画像も格納されています.その JPEG 画像を取得して表示したのがその次の図です.リソースの型を RT_RCDATA に変更して取得しています.



図10
リソースファイル DlgSetRes.dres からビットマップ画像を取得
図11
上と同じリソースファイルから JPEG 画像を取得
  • リソースの型を RT_RCDATA にして取得

リスト6
unitResFile ユニットを利用してリソースファイルからビットマップを取得
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, StrUtils;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    ListBox1: TListBox;
    Image1: TImage;
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure ListBox1Click(Sender: TObject);
  private
    { Private 宣言 }
    FResouceFile : string;
    FResType     : string;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  unitResFile,
  unitResourceDetails;

{$R *.dfm}

//=============================================================================
//  フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
  Edit1.Text := 'DlgSetRes.dres';
  Image1.AutoSize :=True;
end;

//=============================================================================
//  リソースの列挙
//  ここでは RT_BITMAP とする(つまりビットマップ)
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LResModule : TResModule;
  LResIndex  : Integer;
  LresIDName : string;
begin
  Image1.Picture.Assign(nil);

  FResouceFile := ExpandFileName(Trim(Edit1.Text));
  FResType     := UIntToStr(Cardinal(RT_BITMAP));

  ListBox1.Items.Clear;
  ListBox1.Items.BeginUpdate;

  LResModule := TResModule.Create;
  LResModule. LoadFromFile(FResouceFile);
  try
    //リソース名の列挙
    for LResIndex := 0 to LResModule.ResourceCount - 1 do begin
      if LResModule.ResourceDetails[LResIndex].ResourceType = FResType then begin
        LresIDName := LResModule.ResourceDetails[LResIndex].ResourceName;
        ListBox1.Items.Add(LresIDName);
      end;
    end;
  finally
    FreeAndNil(LResModule);
  end;

  ListBox1.Items.EndUpdate;
  Label1.Caption := IntToStr(ListBox1.Items.Count);
end;

//=============================================================================
//  ListBox1のOnClickイベント
//  リストボックスのItemをクリックしたらそのリソースを読み出してTImageに表示
//=============================================================================
procedure TForm1.ListBox1Click(Sender: TObject);
var
  LIndex          : Integer;
  LResModule      : TResModule;
  LResIDName      : string;
  LResIndex       : Integer;
  LresStream      : TBytesStream;
  LPictStream     : TBytesStream;
  LBMCheckStr     : array[0..1] of AnsiChar;
  LInfoHeaderSize : DWORD;
  LresInfoHeader  : TBitmapInfoHeader;
  LBmpFileHeader  : TBitmapFileHeader;
  LWICImage       : TWICImage;
  LFlag           : Boolean;
begin
  Image1.Picture.Assign(nil);
  LIndex := ListBox1.ItemIndex;
  if FResouceFile = '' then exit;
  if LIndex < 0 then exit;

  LresStream := TBytesStream.Create;
  LResModule := TResModule.Create;
  try
    LResModule. LoadFromFile(FResouceFile);
    //指定したリソース名のインデックスを取得
    LFlag := False;
    for LResIndex := 0 to LResModule.ResourceCount - 1 do begin
      if LResModule.ResourceDetails[LResIndex].ResourceType = FResType then begin
        LResIDName := LResModule.ResourceDetails[LResIndex].ResourceName;
        if LresIDName = ListBox1.Items[LIndex] then begin
          LIndex := LResIndex;
          LFlag  := True;
          Break;
        end;
      end;
    end;
    if LFlag = False then exit;

    //そのインデックスのリソースをTMemoryStreamに保存
    LresStream.Clear;
    LResModule.ResourceDetails[LIndex].Data.SaveToStream(LresStream);

    LresStream.Position := 0;
    LresStream.ReadBuffer(LInfoHeaderSize, 4);

    //ビットマップのファイルヘッダ部が付いているビットマップリソースもある
    //そのチェック
    LresStream.Position := 0;
    LresStream.Read(LBMCheckStr, 2);
    if LBMCheckStr <> 'BM' then LBMCheckStr := '';

    LPictStream := TBytesStream.Create;
    LWICImage   := TWICImage.Create;
    try
      //ファイルヘッダ情報がないビットマップデータ
      if (LBMCheckStr = '') and (LInfoHeaderSize <= SizeOf(TBitmapV5Header)) then begin
        //ヘッダ部分を読み出す
        LresStream.Position := 0;
        LresStream.Read(LresInfoHeader, SizeOf(LresInfoHeader));

        //TBitmapFileHeaderヘッダに値をセット
        //$4D42はBMのASCII文字コード(リトルエンディアンなので$42がB,$4DがM)
        LBmpFileHeader.bfType      := $4D42;
        LBmpFileHeader.bfSize      := LresStream.Size;
        LBmpFileHeader.bfReserved1 := 0;
        LBmpFileHeader.bfReserved2 := 0;
        LBmpFileHeader.bfOffBits   := SizeOf(TBitmapFileHeader);

        //別のTMemoryStreamにTBitmapFileHeaderの情報を書き込んで
        //その後に読み出したリソースのデータを追加
        LPictStream.Position := 0;
        LPictStream.Write(LBmpFileHeader, SizeOf(TBitmapFileHeader));
        LresStream.Position := 0;
        LPictStream.CopyFrom(LresStream, LresStream.Size);
      end else begin
        //読み出したデータを別のTMemoryStreamにコピー
        LPictStream.Position := 0;
        LresStream.Position  := 0;
        LPictStream.CopyFrom(LresStream, LresStream.Size);
      end;

      //画像の表示
      LWICImage.LoadFromStream(LPictStream);
      Image1.Picture.Assign(LWICImage);
    finally
      FreeAndNil(LPictStream);
      FreeAndNil(LWICImage);
    end;
  finally
    FreeAndNil(LResModule);
    FreeAndNil(LresStream);
  end;
end;

end.




05_リソースの操作とリソースエディタ


本ページのサンプルのように,EXE, DLL 等の実行形式のファイルからリソースとしてビットマップ等を取り出すには EnumResourceNames 等のリソース操作用の関数を使用しますが,PE ヘッダの情報からも取得できます.
以下の [EXE または DLL 内の...] の記事で紹介しているリソースエディタと呼ばれるプログラムを使用すると,それらのリソースをツリー構造で表示できます.エディタですから編集もできます.



上の参考リンクにある XN Resource Editor は Delphi で開発されていてソースコード付きです.

ResEdit は 32 ビット版と 64 ビット版がありますが,64 ビットの Windows では 32 ビット版は動作しません.起動時に [Wizard] のダイアログが現れますがキャンセルで構いません.[Include Paths] の設定ダイアログはリソース作成に使用する Microsoft SDKs, Microsoft Visual Studio の設定です.これもキャンセルで閉じて構いません.後でも設定できます.
対象のファイルは [File] [Open Project] で開きます.[Options] [Preferences...] のダイアログで [Japanese] を選択して,ResEdit を再起動するとメニューが日本語になります.

Resource Bulider, PE Explorer は Delphi 製です.
リソースエディタと呼べるアプリケーションは他にもあります.