リソース


アイコンリソース

これまで、実行ファイルやウィンドウのタイトルバーに左上部分
タスクバーに表示されるアイコンは全てウィンドウズが用意しているものを使いました
今回は、このアイコンを解説したいと思います

今まで、独自のアイコンを持った実行ファイルをなんども見たことがあるでしょう
では、どのようにすれば自分の実行ファイルに独自のアイコンを表示させるのでしょう?
その秘密はリソースにあるのです

Windows の実行ファイルは実行ファイル内にデータを含められるのです
アイコンやマウスカーソルはリソースと呼ばれるデータで、通常は実行ファイルに含まれています
リソースは、事実上文字列やビットマップを含ませることができるので、非常に重要な技術です

リソースは、まずリソーススクリプトというものを記述します
これは、プログラムを書くようにテキストエディタで記述し .RC という拡張子で保存します
このファイルは、Cコンパイラではなく リソースコンパイラ でコンパイルします

リソースコンパイラでリソーススクリプトをコンパイルすると
.RES という拡張子を持つバイナリファイルが生成されます
最終的には、これを実行ファイルにリンカで組み込むことによって完成です

リソーススクリプトとはテキストで表現されたリソースの情報です
アイコンリソースを実行ファイルに埋めこむには ICON 文を使います

nameID ICON filename

nameID には、16ビットの符号なし整数か文字列を指定します
プログラムがリソースを呼び出すとき、これを識別子とします
通常、整数を利用する場合は別にヘッダファイルを生成して定数を定めます

filename は、アイコンリソースの名前です
一般にアイコンは 32×32 が標準なので、今回もこのサイズのアイコンを使います
アイコンは、アイコンエディタなどを用いて適当に描いてください

ところで、一般的に識別用のIDは整数です
しかし、アイコンリソースは文字列の識別子を持つことができます
これは LoadIcon() 関数の第二引数が LPCTSTR 型である事からも予想できます

この時、文字列はクォーテーションで囲む必要はありません
#define によって数値定数が認識されなければ、スクリプトは文字列識別子と認識します
さっそく、実行ファイルにアイコンリソースを入れるスクリプトを記述してみましょう
//test.rc
KITTY ICON DISCARDABLE "test.ico"
さて、今回のリソーススクリプトはこの1行だけで終わりです
コメントやインクルードなどの構文はC同様に使えます
//ヘッダファイルは、識別コード用の定数を読みこむのによく使われます

このリソーススクリプトは、"test.ico" という名前のアイコンリソースにKITTY という識別子を付けています
LoadIcon() 関数から呼び出す時にこの識別子を用います

DISCARDABLE キーワードは、Windowsが必要によって
メモリからアイコンを破棄しても良いということを明示するものです
この場合でも、Windows はアイコンが必要になればリロードしてくれます
デフォルトの値なので、省略してもかまいません

次に、プログラム部分を記述します
どの様なリソースも、同じ実行ファイルに組み込まれていると言え
変数の様に直接アクセスすることはできません

リソースは、何らかの Windows ファンクションを使うなどして読み込みます
今回のアイコンは LoadIcon() ファンクションがこれを遂行します
#include <windows.h>

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(hInstance , TEXT("KITTY"));
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


コンパイル後の、実行ファイルの状態とウィンドウです
test.ico ファイルを実行ファイルにリソースとして埋めこんでいます
実行ファイルのアイコンが、カスタムアイコンになっていますね

また、ウィンドウ左上のアイコンも同様にカスタムアイコンになっています
当然、タスクバーのアイコンも同じアイコンになっています

LoadIcon() の第一引数は、リソースが入っているモジュールのインスタンスハンドルです
今回は、実行ファイルに埋めこんでいるため自分自身のインスタンスハンドルを指定しています


※カスタムアイコンが表示されない場合
総合開発環境では、ビルドするだけで問題なくアイコンが表示されるでしょう
しかし、コマンドラインツールではコンパイルだけではダメです
指定のリソースリンカを使って、実行ファイルにリソースを埋めこむ必要があります

Borland C++ Compiler では、実行ファイルを生成した後に
BRC32.EXE でコンパイルとリンクを行ってください
BRC32 コマンドはコンパイルとリンクを同時に行ってくれます

brc32 test.rc test.exe

これで、上のリソースをコンパイルし
実行ファイルにリソースを埋め込むことができます


アイコン識別子に整数を使う

前回は、アイコンの識別子はそのままの文字列を使いました
しかし、プログラマの好みの問題ですが数値がよいという人もいるでしょう

一般的に、数値を使う場合は #define で定めた定数を使います
このとき、別に定数専用のヘッダファイルを作ります
VC++ のファイル管理方法では Resource.h というヘッダファイルを生成することから
ファイル名はこの名前にすることが一般的といえるでしょう

#define を使ってアイコンの番号を定めたら
次は、リソーススクリプトでまず識別子を定義したヘッダファイルをインクルードし
ICON 文の nameID で定数を指定します

問題は、LoadIcon() 関数で識別子を指定する時です
LoadIcon() の第二引数は LPCTSTR 型なので文字列でなければ行けません
そこで MAKEINTRESOURCE マクロを使います

LPTSTR MAKEINTRESOURCE(WORD wInteger);

wInteger には、16ビットの整数を指定します
戻り値は wInteger を LPTSTR にキャストした形です
正確には、次のような作業を行っています

#define MAKEINTRESOURCE(i) (LPTSTR) ((DWORD) ((WORD) (i)))

じつは、LoadIcon() は上位ワードが 0 であれば数値識別子であると判断するのです
だから、アイコンの識別子は16ビット値である必要があったのです
//resource.h
#define IDI_ICON 101
//test.rc
#include "resource.h"

IDI_ICON ICON DISCARDABLE "test.ico"
//test.c
#include <windows.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd , msg , wp , lp);
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	HWND hwnd;
	MSG msg;
	WNDCLASS winc;

	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= winc.cbWndExtra	= 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(hInstance , MAKEINTRESOURCE(IDI_ICON));
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("KITTY");

	if (!RegisterClass(&winc)) return -1;

	hwnd = CreateWindow(
			TEXT("KITTY") , TEXT("Kitty on your lap") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hwnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}
このようにすれば、数値をアイコンの識別子に使えます
結果は先のプログラム同じです


MAKEINTRESOURCE

LPTSTR MAKEINTRESOURCE(WORD wInteger);

この関数はマクロです
整数値をリソース管理関数と互換性のあるリソースタイプに変換します

wInteger - 変換する整数値

戻り値 - 指定された値の下位ワード(上位ワードは 0)



前のページへ戻る次のページへ