プログラムで使うさまざまな画像や音楽、テキストなどを外部ファイルを参照することなく実行ファイルに埋め込むために、ひとつには Delphi IDE の機能をつかってコンポーネントに予めオブジェクトインスペクタから読み込んでおく方法がある。ここの TPicture と TStringList のカプセル化 では、これを利用して画像やテキストを取り込むために、TPicture と TStringList をカプセル化したコンポーネントを作った。これは、コンポーネントの機能が *.dfm ファイルに取り込まれることを利用した一種のリソースの使い方と言えるだろう。この方法の欠点は、*.dfm ファイルが読みにくくなる他に、実行時にコンポーネントが生成されるときにストリーミングにより読み込まれるため、大きな画像ファイルやテキストファイルを設定したとき起動が遅くなったり、実行時のメモリイメージが大きくなったりすることだ。しかし、簡単で手軽にできるのは大きな利点である。
もう一つの方法は、参照元の Win32API の稿のように、ビットマップやアイコンをリソースファイルとしてコンパイル時にリンクして EXE に取り込み、実行時に取り出して使う方法である。この方法の方がウィンドウズプログラム作成法の標準手順なのである。画像やテキストを取り出したり破棄するタイミングをコードで決められるので、起動に時間がかかることはなく、また、実行時のメモリを有効に使うことができる。参照元では、Delphi3.1 であったのでリソースのコンパイルなどは、プログラムの作成とは別の手順でコマンドラインからリソースコンパイラをつかってコンパイルする必要があり、かなり面倒であった。筆者は現在自宅では Delphi5 のユーザなので、リソースファイルの取り扱いが非常に楽になっている。IDE からプロジェクトに、リソーススクリプトファイル (*.rc) を加えるだけで、コンパイル時に自動的にリソースファイル (*.res) が生成され、またリンクも自動的に行われる。この手順は、非常に簡単なので覚えてしまうと応用範囲は大きいだろう。
それはさっそくやってみよう。Delphi を起動してすぐに適当なディレクトリに保存する。ここでは ビットマップファイルを取り込んでみよう。任意のビットマップファイルをプログラムを保存したディレクトリにコピーしておく。ここでは、WEB からダウンロードしたフリー素材の donaldbut.bmp というファイルを使う例を示す。つぎに、メモ帳などのテキストエディタで
Donald BITMAP "donaldbut.bmp"
という一行の内容を書いて、やはりプログラムを保存したディレクトリに MyRes.rc という名前で保存する。ファイルの名前は何でもよい(日本語名は試してない)が、拡張子は .rc であることが必要である。つぎに、Delphi IDE の[プロジェクトに追加] メニューを選んで、右図のように表示されるダイアログの一番下の ファイルの種類のところを 「リソース ファイル(*.rc)」にして、先ほどの MyRes.rc を選ぶ。
そうすると、右図のようにプロジェクトファイルにこれがリソースとしてリンクされるコンパイラ指令が自動的に書き加えられる。これで前準備の手順はおしまいである。とっても簡単! あとはコンパイルするとこのビットマップが自動的に exe に取り込まれ、コードで取り出して使うことができる。
exe の中に取り込まれたビットマップを取り出してみよう。ここでは TBitmap に取り出して、Form1 の Canvas に描いてみよう。
List 1
procedure TForm1.Button1Click(Sender: TObject); var BM:TBitmap; begin BM := TBitmap.Create; try BM.LoadFromResourceName(hInstance,'Donald'); Canvas.Draw(10,10,BM); finally BM.Free; end; end;
これで Button1 をクリックすると右図のようになる。ここでは TBitmap の LoadFromResourceName() メソッドでリソースから画像を取り出している。
Image1 に表示したいときは、以下のようにする。
List 2
procedure TForm1.Button2Click(Sender: TObject); begin Image1.Picture.Bitmap.LoadFromResourceName(hInstance,'Donald'); end;
このように非常に簡単に画像ファイルなどを exe に埋め込んで、そしてコードで取り出して使うことができる。画像が埋め込まれていることを確認するために、元画像ファイルを削除しても同様に動作することが確かめられる。
リソーススクリプトファイルはテキストエディタで以下のような書式で書く。
リソース名 リソースタイプ "ファイル名"
リソース名は任意の文字列である。リソースタイプも任意にできるがなるべく内容が分かるようにする。ビットマップやアイコンなどの標準リソースタイプは BITMAP や ICON などにしておいた方が無難である。ダブルクォートでかこまれたファイル名は取り込むファイルを指定する。プログラムと同じ場所にあるときはパスを省略できる。そうでないときは、フルパスで指定する。それぞれの文字列は一つ以上空白で区切り、一つのリソースは一行で書く。複数のリソースを設定するときは、この書式を複数書く。ここでは、アイコンを二つ追加してみよう。MyRes.rc を以下のようにする。
Donald BITMAP "donaldbut.bmp" Chemical ICON "C:\Home\Appl\Delphi5\Borland Shared\Images\Icons\chemical.ico" Chip ICON "C:\Home\Appl\Delphi5\Borland Shared\Images\Icons\chip.ico"
そして、表示するためのコードを以下に示す。
List 3
procedure TForm1.Button1Click(Sender: TObject); var BM:TBitmap; begin BM := TBitmap.Create; try BM.LoadFromResourceName(hInstance,'Donald'); Canvas.Draw(10,10,BM); finally BM.Free; end; end; procedure TForm1.Button2Click(Sender: TObject); var IC:TIcon; begin IC := TIcon.Create; try IC.Handle := LoadIcon(hInstance,'Chemical'); Canvas.Draw(100,10,IC); IC.Handle := LoadIcon(hInstance,'Chip'); Canvas.Draw(140,10,IC); finally IC.Free; end; end;
実行して、Button1 と Button2 を押すと右図のようになる。アイコンは LoadIcon() API でハンドルに代入する。
このときに自動生成された MyRes.res ファイルを Delphi 付属の Image Editor で開いてみると、右図のように、ちゃんとアイコンが二つ追加されてコンパイルされている事が分かる。
それでは、この方法でいろいろなファイルをリソースにしてみよう。まず、Jpeg 画像から試してみよう。リソーススクリプトファイルをたとえば以下のようにする。
MyJpeg JPEG "C:\Documents and Settings\My Documents\My Pictures\サンプル.jpg"
そして、以下のようなコードで取り出して表示する。
List 4
uses Jpeg; procedure TForm1.Button1Click(Sender: TObject); var RS:TResourceStream; JPG:TJpegImage; begin RS := TResourceStream.Create(hInstance,'MyJpeg','JPEG'); JPG := TJpegImage.Create; try JPG.LoadFromStream(RS); Canvas.Draw(10,10,JPG); finally JPG.Free; RS.Free; end; end;
実行結果を右図に示す。TJpegImage クラスには、TBitmap のような直接リソースにアクセスするメソッドがないので、ここでは、TResourceStream を経由して取り出している。このように、ウィンドウズ標準のリソース以外では、TResourceStream クラスを経由すると、任意のタイプのリソースを取り出すことができる。逆に、ビットマップやアイコンのような標準リソースの場合は TResourceStream はロードに失敗するようである。
今度は、長いテキストファイルをリソースにしてみよう。以下にその *.rc ファイルの例をしめす。
MyText TEXT "C:\Home\Util\01\Lhaca\readme.txt"
これを TStrings の派生クラスに取り込むには以下のようにする。
List 5
procedure TForm1.Button1Click(Sender: TObject); var RS:TResourceStream; begin RS := TResourceStream.Create(hInstance,'MyText','TEXT'); try Memo1.Lines.LoadFromStream(RS); finally RS.Free; end; end;
ここでは、TResourceStream を経由して、TMemo クラスの Lines.LoadFromStream() で読み込んでいる。右図に実行結果をしめす。
つぎに、*.wav ファイルを取り込んでみよう。*.rc の例を以下に示す。
MySound WAVE "C:\Home\Util\Game1\Smash18\Smash18\Data\WAVE\GameOver.wav"
これを演奏するには以下のようにする。
List 6
uses MMSYSTEM; procedure TForm1.Button1Click(Sender: TObject); var RS:TResourceStream; begin RS := TResourceStream.Create(hInstance,'MySound','WAVE'); try sndPlaySound(RS.Memory,SND_MEMORY or SND_SYNC); finally RS.Free; end; end;
このように、TResourceStream クラスを仲介することを前提にすると、ほとんどどんなファイルでもリソースとして exe ファイルに埋め込むことができるようである。筆者が試したところでは、*.exe 、*.doc 、*.dll などを取り込み、実行時に TResourceStream クラスに取り出して、その SaveToFile() で外部ファイルに書き出して、起動したり、LoadLibray() で使うこともできるようだ。この例として Acrobat の pdf ファイルを試してみよう。*.rc ファイルの例を以下に示す。
MyPDF PDF "Test.pdf"
これを実行時に取り出して、ファイルに保存し、Reader を起動して読み込むまでのコードは以下のようになる。
List 7
uses ShellAPI; procedure TForm1.Button1Click(Sender: TObject); var RS:TResourceStream; begin RS :=TResourceStream.Create(hInstance,'MyPDF','PDF'); try RS.SaveToFile(ExtractFilePath(ParamStr(0))+'MyPDF.pdf'); finally RS.Free; end; if FileExists(ExtractFilePath(ParamStr(0))+'MyPDF.pdf') then ShellExecute(0,'open',PChar(ExtractFilePath(ParamStr(0))+'MyPDF.pdf'),nil,nil,SW_SHOWNORMAL); end; procedure TForm1.FormDestroy(Sender: TObject); begin if FileExists(ExtractFilePath(ParamStr(0))+'MyPDF.pdf') then DeleteFile(ExtractFilePath(ParamStr(0))+'MyPDF.pdf'); end;
実行結果を右図にしめす。ここでは、FormDestroy() で書き出したファイルを削除している。
以上のように、リソースファイルをつくって実行ファイルの中に取り込み、実行時にコードで取り出して使う、ということが思いの外、簡単にできることが分かった。