ツールバーリソースの取得

はじめに

 Windowsプログラミングをしているとき"すごい"と思うことの一つにツールバーがある。リソースエディタ で簡単にアイコンの絵を描いただけでビルドするとツールバーに新しいアイコンを追加できる。
 文字列とコマンドID、そして画像をきちんと結びつけた状態で簡単に管理することができる。しかしこんな にすばらしい機能があるのに、ツールバーだけで使われているだけでほかの場面での出番はない。

 というよりもリソースを呼び出して活用する術がなかなかない。もちろんツールバーに対してSendMessageで TB_GETIMAGELISTコマンドを送りつけてイメージリストを取得することはできる。しかしこれはツールバーが ウインドウとして作られていないとダメという欠点がある。

 ということで直接リソースからツールバーに関連付けられている情報を取得する関数を作成した。

 処理の流れ自体はWTLのソースコードをそのまま利用している。



解説

 WTLなどで自動的に生成されるツールバーリソースは以下のようになる。
	IDR_MAINFRAME TOOLBAR  16, 15
	BEGIN
	    BUTTON      ID_FILE_NEW
	    BUTTON      ID_FILE_OPEN
	    BUTTON      ID_FILE_SAVE
	    SEPARATOR
	    BUTTON      ID_EDIT_CUT
	    BUTTON      ID_EDIT_COPY
	    BUTTON      ID_EDIT_PASTE
	    SEPARATOR
	    BUTTON      ID_FILE_PRINT
	    BUTTON      ID_APP_ABOUT
	END

 太字部分から分かるようにツールバーはRT_TOOLBARという形式でリソースファイルに保存されている。
 つまり::FindResource(hInstance,MAKEINTRESOURCE(nResourceID),(LPTSTR)RT_TOOLBAR);を使えば リソースに直接アクセスすることができる。
 ここで取り出せるバイナリ形式のツールバーに関するデータは

	struct _ToolBarData
	{
		WORD wVersion;				//常に1
		WORD wWidth;				//画像(ボタン1つ)の幅
		WORD wHeight;				//画像の高さ
		WORD wItemCount;			//ツールバーアイテム数(セパレータとボタンの総数)
		WORD aItems[wItemCount]		//コマンドID(セパレータは0)
	};

 このような形の構造体で保存されている。
 実際の画像はRT_BITMAP、文字列はSTRINGTABLEとして格納されている。文字列の取得はコマンドID が分かれば簡単なのでここではBITMAP画像を取り出してIMAGELISTに格納するまでをしている。



使い方


 画面の座標(100,100)にツールバーの1つ目の画像(アイコン)を描画する。
 CDnpArrayExはMFCのCArray互換クラス。

void	Test()
{
	HDC					hDC;
	HIMAGELIST			hImageList;
	CDnpArrayEx	anID;

	hImageList = NULL;
	LoadToolbarResource(_Module.m_hInst,IDR_MAINFRAME,hImageList,anID);

	hDC = ::GetDC(NULL);
	::ImageList_Draw(hImageList,0,hDC,100,100,ILD_NORMAL | ILD_TRANSPARENT);
	::ReleaseDC(NULL,hDC);
}



ソースコード


	//
	//	ツールバーリソースからのイメージリスト&コマンドID取得
	//
	//	WTLのatlctrlw.h内のCCommandBarCtrlImpl::_LoadImagesHelper()をほぼそのまま利用している
	//
	//	nResourceIDに示されるツールバーリソースの....
	//
	//	ボタンの画像をhImageListに、コマンドIDをanIDに出力する。
	//	画像のマスクはcolorMaskにより設定する。
	//
	bool	LoadToolbarResource(HINSTANCE hInstance,UINT nResourceID,HIMAGELIST& hImageList,CDnpArrayEx& anID,COLORREF colorMask=RGB(0xc0,0xc0,0xc0))
	{

		//
		//リソース保存形式
		//
		struct _ToolBarData	// toolbar resource data
		{
			WORD wVersion;
			WORD wWidth;
			WORD wHeight;
			WORD wItemCount;
			//WORD aItems[wItemCount]

			WORD* items()
				{ return (WORD*)(this+1); }
		};

		WORD*			pItems;
		int				nItems;
		CBitmap			bmp;
		BITMAP			sBitmap;
		UINT			nFlags;
		HRSRC			hRsrc;
		HGLOBAL			hGlobal;
		_ToolBarData*	pData;

		//引数で渡すhImageListはNULLでない場合Destroyする。
		//そのためhImageListはNULLで渡した方が安全!
		ATLASSERT(hImageList == NULL);					//NULLでない場合は警告を出す
		if(hImageList)
			::ImageList_Destroy(hImageList);			//ImageListの破壊
		hImageList = NULL;

		//ID格納用配列の破壊
		anID.RemoveAll();

		//ツールバーリソースを探す
		hRsrc = ::FindResource(hInstance,MAKEINTRESOURCE(nResourceID),(LPTSTR)RT_TOOLBAR);
		if(hRsrc == NULL)
			return false;

		//ツールバーリソースの読み込み
		hGlobal = ::LoadResource(hInstance, hRsrc);
		if(hGlobal == NULL)
			return false;

		//ツールバーリソースにアクセスするためのロック
		pData = (_ToolBarData*)::LockResource(hGlobal);
		if(pData == NULL)
			return false;

		ATLASSERT(pData->wVersion == 1);	//ツールバーリソースの保存形式バージョンのチェック。通常は1。将来的に2とか出るかも...

		pItems = pData->items();			//ボタンに割り当てられたIDの配列
		nItems = pData->wItemCount;			//ツールバーアイテム数(セパレータを含むので画像の数とは一致しない!)

		//ツールバー画像の取り込み
		bmp = (HBITMAP)::LoadImage(hInstance,MAKEINTRESOURCE(nResourceID), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE);

		ATLASSERT(bmp.m_hBitmap != NULL);
		if(bmp.m_hBitmap == NULL)			//ツールバー画像の取り込み失敗
			return false;

		//ツールバー画像の情報取得
		ZeroMemory(&sBitmap,sizeof(BITMAP));
		bmp.GetBitmap(&sBitmap);

		if(sBitmap.bmBitsPixel >= 24)
			nFlags = ILC_COLOR24;
		else if(sBitmap.bmBitsPixel == 16)
			nFlags = ILC_COLOR16;
		else if(sBitmap.bmBitsPixel == 8)
			nFlags = ILC_COLOR8;
		else if(sBitmap.bmBitsPixel == 4)
			nFlags = ILC_COLOR8;
		else
			nFlags = ILC_COLOR8;
		nFlags |= ILC_MASK;

		//ImageListの作成
		hImageList = ::ImageList_Create(pData->wWidth,pData->wHeight,nFlags,sBitmap.bmWidth/pData->wWidth,5);
		if(hImageList == NULL)
			return false;

		//ツールバー画像をイメージリストに代入
		//マスク色の設定も!
		if(::ImageList_AddMasked(hImageList,bmp,colorMask) == -1)
		{
			::ImageList_Destroy(hImageList);
			hImageList = NULL;

			return false;
		}

		//ツールバーのコマンドIDを配列に格納
		for(int i = 0; i < nItems; i++)
		{
			if(pItems[i] == 0)		//セパレータはゼロなので無視
				continue;
			anID.Add(pItems[i]);
		}

		//ツールバーのアイコン数とコマンドIDの数が一致するかのチェック
		if(::ImageList_GetImageCount(hImageList) != anID.GetSize())
		{
			//数が一致しなかった!
			anID.RemoveAll();

			::ImageList_Destroy(hImageList);
			hImageList = NULL;

			return false;
		}

		return	true;
	}




カテゴリー「VC++ TIPS」 のエントリー