カスタムリソース

その他の形式のリソース

これまでビットマップと文字列のリソースへの追加を説明しました。
Visual Studioではその他に画像ファイル(.jpg、.pngなど)」「アイコンファイル(.ico)」「カーソルファイル(.cur)」「WAVEファイル(.wav)」「HTMLファイル(.htm、.html)」などをリソースに追加できます。
追加の手順はビットマップのときと同じで、リソースの追加ダイアログから「インポート」を押下してファイルを選択します。
リソースの追加ダイアログ

ファイルの選択ダイアログでは、デフォルトではビットマップ(などの画像ファイル)のみが表示されるようにフィルタリングされています。
右下のファイルの種類を変更して、目的のファイルを表示させてください。
リソースの追加ダイアログから読み込み可能なファイル形式

ファイルを選択するとリソースファイル(リソーススクリプトとヘッダーファイル)に情報が追加されます。
ファイルの選択後、そのファイルを選択するためのエディタが表示されることがあります。
(ビットマップの場合はビットマップエディタが表示される)
必要がなければそのまま閉じてください。

カスタムリソース

上述のファイル選択ダイアログでフィルター(ファイルの種類の選択)に「すべてのファイル」を指定すると、文字通りファイルの種類に関係なく全てのファイルが表示されます。
Visual Studioが対応していない形式のファイルを選択すると、カスタムリソースを使用してリソースに追加することになります。

非対応ファイルを選択すると、以下のようなダイアログが表示されます。
ファイルの種類の入力ダイアログ1

上の図は「sample.zip」というZIPファイルを選択したところです。
「リソースの種類」にはファイルの種類を識別するための任意の文字列を入力します。
ここでは「ZIP」という文字列にします。
ファイルの種類の入力ダイアログ2

入力欄の下の空欄は、すでに追加したカスタムリソースがある場合に、追加したファイルの種類の一覧が表示されます。
その一覧からファイルの種類を選択することもできます。

「OK」をクリックすると以下のようなバイナリエディタが表示されます。
バイナリエディタとは、ファイルの中身を1バイトずつ数値(16進数)で表示および編集できるものです。
ここでは編集はしないのでそのまま閉じてください。
バイナリエディタ

これでカスタムリソースの追加は完了です。
リソーススクリプトおよびリソースヘッダーファイルを開くと以下のような項目が追加されています。


/////////////////////////////////////////////////////////////////////////////
//
// ZIP
//

IDR_ZIP1                ZIP                     "sample.zip"

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ で生成されたインクルード ファイル。
// Resource.rc で使用
//
#define IDR_ZIP1                        101

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

「ZIP」がファイルの種類、「IDR_ZIP1」が追加したカスタムリソースの識別子です。
これらを使用してコードからリソースを読み込みます。

詳細は不明ですが、読み込むファイルの種類(内容?)によってはリソースの追加のダイアログからリソースを追加できないことがあるようです。
(ファイルの読み込みに失敗したというメッセージダイアログが表示される)
その場合は、上記コードを参考にして手動でリソーススクリプトとヘッダファイルを編集してください。

カスタムリソースの読み込み

ビットマップなどの標準のリソースは、それを読み込むための関数(LoadImage関数など)が用意されていることがありますが、カスタムリソースの場合はFindResource関数、LoadResource関数、LockResource関数、SizeofResource関数の四つを使用します。
カスタムリソースの使用は

  • リソースを検索して、
  • リソースを読み込んで、
  • ポインタを取得してアクセス

という手順で行います。

FindResource関数

HRSRC FindResourceW(
 HMODULE hModule,
 LPCWSTR lpName,
 LPCWSTR lpType
);
モジュールhModuleから文字列lpNameで識別されるファイルタイプlpTypeのリソースの情報を取得する。

hModuleはリソースが含まれるインスタンスハンドルを指定します。
通常はアプリのインスタンスハンドルです。
lpNameはリソース名(文字列)、もしくはリソースの識別子をMAKEINTRESOURCEマクロで変換したものを指定します。
(LoadBitmap関数の項を参照)
lpTypeはリソースの種類を示す文字列です。

この関数はHRSRC型を返します。
この戻り値は次のLoadResource関数、およびSizeofResource関数で使用します。
リソースの取得に失敗した場合はNULLを返します。

LoadResource関数

HGLOBAL LoadResource(
 HMODULE hModule,
 HRSRC hResInfo
);
モジュールhModuleからリソースhResInfoを読み取り、そのハンドルを返す。

hModuleFindResource関数と同じです。
hResInfoFindResource関数の戻り値を指定します。

この関数はHGLOBAL型を返します。
この戻り値は次のLockResource関数で使用します。
ハンドルの取得に失敗した場合はNULLを返します。

なお、この関数で読み込んだリソースは解放の必要はありません。

LockResource関数

LPVOID LockResource(
 HGLOBAL hResData
);
リソースハンドルhResDataへのポインタを取得する。

hResDataLoadResource関数の戻り値を指定します。
戻り値はLPVOID型で、指定のリソースへのポインタです。
(内部的にはvoid*型)
このポインタを通してリソースを読み取ることができます。
失敗した場合はNULLを返します。

なお「Lock」という名前になっていますが実際にはロック(排他アクセス)は行いません。

SizeofResource関数

LockResource関数でポインタを取得出来ても、リソースのサイズが分からないとどこまでを読み取れば良いのかが分かりません。
(NULL文字のような終端を表すデータはない)
リソースのサイズはSizeofResource関数で取得します。

DWORD SizeofResource(
 HMODULE hModule,
 HRSRC hResInfo
);
モジュールhModule内のリソースhResInfoのサイズを取得する。

hModuleFindResource関数と同じです。
hResInfoFindResource関数の戻り値を指定します。
(LoadResource関数の戻り値ではないことに注意)

戻り値はリソースのバイト数です。
失敗した場合は0です。

サンプルコード

これらの関数を使用してリソースからZIPファイルを読み込むサンプルコードを示します。
読み込んだデータの中身を16進数とASCII文字で表示します。


#include <windows.h>
#include <strsafe.h>

#include "resource.h"

//ウィンドウの生成等は省略

//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static WCHAR* binary;
	static WCHAR* ascii;

	switch (message)
	{
	case WM_CREATE:
	{
		//リソースの検索
		HRSRC hRsrc = FindResource(hInst, MAKEINTRESOURCE(IDR_ZIP1), L"ZIP");
		if (hRsrc == NULL)
			break;

		//リソースの読み込み
		HGLOBAL hResource = LoadResource(hInst, hRsrc);
		if (hResource == NULL)
			break;

		//ポインタの取得
		LPVOID pZip = LockResource(hResource);
		//サイズの取得
		DWORD size = SizeofResource(hInst, hRsrc);

		if (pZip == NULL || size == 0)
			break;

		//1バイトずつ文字列に変換

		//バイナリ(2桁で1バイト)
		binary = malloc(size * (sizeof(WCHAR) * 2) + sizeof(WCHAR));
		//ASCII文字
		ascii = malloc((size + 1) * sizeof(WCHAR));

		//ポインタ演算用
		unsigned char* _pZip = (unsigned char*)pZip;
		WCHAR* _binary = binary;
		WCHAR* _ascii = ascii;

		while (size--)
		{
			//16進数文字(2桁分)
			StringCchPrintf(_binary, 3, L"%02X", *_pZip);

			//印字文字の判定
			if (isprint(*_pZip)) 
				StringCchPrintf(_ascii, 2, L"%c", *_pZip);
			else
				//非印字文字ならドットに置き換え
				StringCchPrintf(_ascii, 2, L".");

			_binary += 2;
			_ascii++;
			_pZip++;
		}
		break;
	}

	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		TEXTMETRIC tm;				//フォントのサイズ情報
		int x1, x2, y;				//テキスト描画位置
		WCHAR* _binary = binary;	//ポインタ演算用
		WCHAR* _ascii = ascii;		//ポインタ演算用

		HDC hdc = BeginPaint(hWnd, &ps);

		//フォントを固定幅に変更
		SelectObject(hdc, GetStockObject(OEM_FIXED_FONT));

		//フォントサイズの取得
		GetTextMetrics(hdc, &tm);
		long charWidth = tm.tmAveCharWidth;	//平均幅
		long charHeight = tm.tmHeight;		//高さ

		//バイナリとASCII文字の左端基準位置
		int x1ref = charWidth;
		int x2ref = charWidth * 3 * 16 + charWidth;
		
		//16バイト出力したら改行
		int count16 = 0;

		x1 = x1ref;
		x2 = x2ref;
		y = charHeight;
		while (*_binary != L'\0')
		{
			TextOut(hdc, x1, y, _binary, 2);
			TextOut(hdc, x2, y, _ascii, 1);

			_binary += 2;
			_ascii++;
			if (++count16 >= 16) {
				count16 = 0;
				x1 = x1ref;
				x2 = x2ref;
				y += charHeight;
			}
			else {
				x1 += charWidth * 3;
				x2 += charWidth;
			}
		}
		EndPaint(hWnd, &ps);
		break;
	}

	case WM_DESTROY: //ウィンドウの破棄
		if (binary) free(binary);
		if (ascii) free(ascii);
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

先ほどのバイナリエディタと同じデータが表示されます。
カスタムリソースの読み込みのサンプル
先ほどのバイナリエディタ
バイナリエディタ

先ほども書いた通り、このリソースは破棄や解放の処理は必要なく、プログラム終了時に解放されます。
(正確にはリソースが含まれるモジュールのアンロード時)

カスタムリソースの新規作成

リソースの追加ダイアログで「カスタム」ボタンを押下するとカスタムリソースを新規作成することができます。
カスタムリソースの新規作成1

カスタムリソースの種類を任意の文字列で指定します。
カスタムリソースの新規作成2

バイナリエディタが開くので、必要なデータを入力します。
以下は「12345678」と入力したところです。
カスタムリソースの新規作成3

ファイルを保存するとリソースの追加が完了します。
データが空のままだとファイルに保存されないので注意してください。
なお、ファイルの拡張子は「.bin」になります。

カスタムリソースの新規作成は、バイナリエディタでデータを入力することになるのであまり使わないでしょう。
別のバイナリエディタからデータをコピー&ペーストするなどの使用方法が現実的です。