VBやVC++では、アプリケーションが使用するビットマップやアイコンなどのイメージ、マルチメディアファイル、テキストなどのいくつかのファイルをリソースとして実行可能ファイルに埋め込むことができます。 これによって外部ファイルとしてそれぞれ単体で存在するものを実行可能ファイルとしてひとまとめにすることができます。 Visual Studio .NETでもリソースを実行可能ファイルに埋め込むことができます。 その方法を順を追って見てみます。
リソースを埋め込むにはリソースとなるファイルが存在している必要があります。 あらかじめ用意しておくこともできますし、数種のリソースはVisual Studio .NETで作成することもできます。 新しくリソースを作成するには、ソリューションエクスプローラでプロジェクトを右クリックして「追加」メニューの「新しい項目の追加」をクリックします。
現れたダイアログの中から追加したい項目を選び、適当なファイル名を与えてやります。 ここではアイコンを選択し、「ApplicationIcon.ico」と名付けることにします。 種類とファイル名を決めたら、「開く」ボタンを押します。
リソースがプロジェクトに追加されるので、あとは生成されたリソースを編集します。
Visual Studio .NETのアイコンエディタなどは非常に使いにくいので、画像などのリソースはフォトショップなど専門のアプリケーションを用いて編集し、完成したものをリソースとして埋め込むことが多いと思います。 既存のリソースはリソースの新規作成と同様の方法で、「追加」メニューの「既存項目の追加」をクリックします。
現れたダイアログの中から目的のファイルを選択します。 選択したら「開く」ボタンを押します。
これで目的のファイルがプロジェクトに追加されました。
このままだとただのプロジェクトファイルなので、プロパティを変更してビルド時にアセンブリに埋め込まれるようにします。 埋め込みたいファイルを選択し、プロパティページで「ビルド アクション」を「埋め込まれたリソース」に変更します。
アセンブリに埋め込まれたリソースを実行時に読み込むには、大まかに次の手順を踏みます。
次のコードではこの手順により先ほど埋め込んだリソースを読み込んでいます。 この例ではSample.txtというテキストファイルもリソースとして埋め込んでいます。
Sample.txt
これはリソースとして埋め込まれたテキストファイルです。
テキストも当然読み込むことができます。
Option Strict On Public Class Form1 Inherits System.Windows.Forms.Form #Region " Windows フォーム デザイナで生成されたコード " #End Region ' アイコン Dim ico As Drawing.Icon ' 画像 Dim img As Drawing.Image Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' 現在実行中のアセンブリを取得 Dim assm As Reflection.Assembly = Reflection.Assembly.GetExecutingAssembly() ' リソースを読み込むためのストリームを宣言 Dim stream As IO.Stream ' アイコンのリソースのストリームを取得 stream = assm.GetManifestResourceStream("ResourceAndMultimedia.ApplicationIcon.ico") ' ストリームを引数として取るコンストラクタを使用 ico = New Drawing.Icon(stream) ' ストリームをクローズ stream.Close() ' 取得したアイコンをフォームのアイコンとして指定 Me.Icon = ico ' イメージのリソースのストリームを取得 stream = assm.GetManifestResourceStream("ResourceAndMultimedia.Sample.jpg") ' ストリームを引数として取るコンストラクタを使用 img = New Drawing.Bitmap(stream) ' ストリームをクローズ stream.Close() ' テキストのリソースのストリームを取得 stream = assm.GetManifestResourceStream("ResourceAndMultimedia.Sample.txt") ' リソースのストリームからStreamReaderオブジェクトを作成 Dim reader As New IO.StreamReader(stream, System.Text.Encoding.GetEncoding(932)) TextBox1.Text = "" ' 一行ずつ読み込み While (reader.Peek <> -1) TextBox1.Text += reader.ReadLine() + vbNewLine End While ' ストリームをクローズ reader.Close() End Sub Private Sub Form1_Paint(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint e.Graphics.Clear(Control.DefaultBackColor) ' フォーム上に画像を描画 e.Graphics.DrawImage(img, 0, 0) End Sub End Class-1) TextBox1.Text += reader.ReadLine() + vbNewLine End While ' ストリームをクローズ reader.Close() End Sub Private Sub Form1_Paint(ByVal sender As System.Object, _ ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint e.Graphics.Clear(Control.DefaultBackColor) ' フォーム上に画像を描画 e.Graphics.DrawImage(img, 0, 0) End Sub End Class]]>
GetManifestResourceStream()メソッドでリソースのストリームを取得する際には、目的のリソースの名前を指定する必要があります。 その名前は「プロジェクト名.ファイル名」となります。 この例ではプロジェクト名が「ResourcesAndMultimedia」なので、たとえば「Sample.jpg」というファイル名のリソースを参照したい場合は「ResourcesAndMultimedia.Sample.jpg」となります。 ちなみに、このメソッドで目的のリソースが発見できなかった場合、Nothingが返されます。
それ以外のソースコードについても、ほとんど問題ないと思います。 BitmapおよびIconクラスはStreamオブジェクトを利用してインスタンスを作成するためのコンストラクタが用意されているため、これに取得できたストリームを渡すだけでイメージのインスタンスを作成することができます。 また、取得できたストリームを元にしてStreamReaderクラスなどのオブジェクトを作成することもできます。 テキストファイルのリソースの読み込みは、この方法を用いて一行ずつ読み込んでいます。
これを実行した結果が上の図です。 小さくて見づらいですが、アイコンもちゃんと変わっています。 下の図はタスクバーの部分を拡大したものです。
リソースをイメージやテキストなどの限定的なオブジェクトとしてではなく、ストリームとして取得できることから、様々な操作を行うことができます。 リソースとしてWaveを埋め込み、独自のアプリケーションの警告音として使用したい場合などにも今まで紹介してきた方法と同じ方法が利用できます。 次のコードでは、アセンブリにSample.wavというファイルを埋め込んでおき、実行時にそれをバイト配列にバッファとして読み込み、ボタンが押された時点でそれを鳴らすということをしています。
Option Strict On Public Class Form1 Inherits System.Windows.Forms.Form #Region " Windows フォーム デザイナで生成されたコード " #End Region ' バッファ(バイト配列)から音を鳴らすためのPlaySound関数 (API関数) <Runtime.InteropServices.DllImport("winmm.dll", EntryPoint:="PlaySound")> _ Private Shared Function PlaySoundFromMemory( _ <Runtime.InteropServices.MarshalAs(Runtime.InteropServices.UnmanagedType.LPArray)> ByVal lpBuffer As Byte(), _ ByVal hModule As Integer, _ ByVal dwFlags As Integer) _ As Integer End Function ' PlaySound関数で再生方法を指定する定数 Private Const SND_ASYNC As Integer = &H1 ' 非同期再生 Private Const SND_MEMORY As Integer = &H4 ' バッファからの再生 ' バッファとしてのバイト配列 Dim soundBuffer() As Byte Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' 現在実行中のアセンブリを取得 Dim assm As Reflection.Assembly = Reflection.Assembly.GetExecutingAssembly() ' リソースのストリームを取得 Dim stream As IO.Stream = assm.GetManifestResourceStream("ResourceAndMultimedia.Sample.wav") ' 取得できたストリームを元にバイナリ形式で読み込むためのBinaryReaderオブジェクトを作成 Dim reader As New IO.BinaryReader(stream) ' バッファにリソースの内容を読み込み soundBuffer = reader.ReadBytes(CInt(reader.BaseStream.Length)) ' ストリームをクローズ reader.Close() End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click ' バッファを再生 PlaySoundFromMemory(soundBuffer, Nothing, SND_ASYNC Or SND_MEMORY) End Sub End Class_ Private Shared Function PlaySoundFromMemory( _ByVal lpBuffer As Byte(), _ ByVal hModule As Integer, _ ByVal dwFlags As Integer) _ As Integer End Function ' PlaySound関数で再生方法を指定する定数 Private Const SND_ASYNC As Integer = &H1 ' 非同期再生 Private Const SND_MEMORY As Integer = &H4 ' バッファからの再生 ' バッファとしてのバイト配列 Dim soundBuffer() As Byte Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' 現在実行中のアセンブリを取得 Dim assm As Reflection.Assembly = Reflection.Assembly.GetExecutingAssembly() ' リソースのストリームを取得 Dim stream As IO.Stream = assm.GetManifestResourceStream("ResourceAndMultimedia.Sample.wav") ' 取得できたストリームを元にバイナリ形式で読み込むためのBinaryReaderオブジェクトを作成 Dim reader As New IO.BinaryReader(stream) ' バッファにリソースの内容を読み込み soundBuffer = reader.ReadBytes(CInt(reader.BaseStream.Length)) ' ストリームをクローズ reader.Close() End Sub Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click ' バッファを再生 PlaySoundFromMemory(soundBuffer, Nothing, SND_ASYNC Or SND_MEMORY) End Sub End Class]]>
まず、Wave音声を再生するためのAPI関数であるPlaySound関数について説明する必要があります。 APIとは何か、またAPIを詳しく知らない方はそういうものだと思って読み飛ばしてもかまいません。 この関数は、Waveファイル・リソース・システムの音声のいずれかのWave音声を鳴らすものです。 この場合のリソースは、ここで使用しているリソースの形式と異なるので利用することはできません。 この関数の一つ目のパラメータは LPCSTR型で、三つ目のパラメータでSND_FILENAMEフラグを指定した場合はファイル名を指定し、SND_MEMORYフラグを指定した場合はバッファとして格納されているWaveイメージへの先頭ポインタを指定します。 この例では後者の方法を利用しています。 また、二番目のパラメータは指定すべき値がないのでNothingにしておきます。
このAPIをVB.NETで使用するため、DllImport属性を利用してインポートしています。 また、バッファからの再生をサポートするため、LPCSTR型である一つ目のパラメータは、MarshalAs属性で配列へのポインタ型へマーシャリングします。 そして、メモリからの再生に特化したPlaySoundであることを示すために、メソッド名をPlaySoundFromMemory()に変更しました。 ちなみに、 C++形式でのPlaySound関数の宣言は次のようになっています。
BOOL PlaySound( LPCSTR pszSound, // 再生対象のサウンド HMODULE hmod, // インスタンスハンドル DWORD fdwSound // 再生フラグ );
リソースからWaveファイルを読み込む場合は、バイト配列として取得することでPlaySound関数が利用可能になるため、BinaryReader クラスのReadBytes()メソッドを利用して直接バイト配列を取得しています。 後は、再生したいときにPlaySoundFromMemory関数にWaveイメージが格納されたバッファと再生フラグを渡すだけで再生することができます。