メニューバーを使う



いろんなソフトのウィンドウの上に張り付いているメニューバー、HSP標準命令では使用できません。
Exforce.dll等のライブラリを使わなければ使用できなかったのですが、Ver2.6よりWinAPIが標準利用できるようになったので、 WinAPIでメニューバーを実現してみましょう。
まずソースです。

;メニューバーを使う

#include "llmod.as"

#module

dim hPopupMenu,16	;16個までのポップアップメニューが作れる

#deffunc setmenu
	mref bmscr,67
	dllproc "CreateMenu",a,0,D_USER
	hMenu=dllret@
	prm=bmscr.13,hMenu
	dllproc "SetMenu",prm,2,D_USER
	dllproc "DrawMenuBar",bmscr.13,1,D_USER
return

#deffunc setpopup str,int
	mref st,32
	string=st
	mref id,1
	mref bmscr,67
	dllproc "CreatePopupMenu",prm,0,D_USER
	hPopupMenu.number=dllret@
	getptr pstring,string
	strlen length,string
	menuiteminfo=44,0x0017,0x0000,0x0000,id,hPopupMenu.number,0,0,0,pstring,length
	;0x0017はMIIM_STATE or MIIM_ID or MIIM_SUBMENU or MIIM_TYPE
	;0x0000はMFT_STRING
	;次の0x0000はMF_ENABLED
	getptr pmenuiteminfo,menuiteminfo
	prm=hMenu,-1,0x0400,pmenuiteminfo	;0x0400はMF_BYPOSITION
	dllproc "InsertMenuItemA",prm,4,D_USER
	dllproc "DrawMenuBar",bmscr.13,1,D_USER
	number++	;ポップアップメニューの個数を増やす
return

#deffunc additem str,int,int,int
	mref st,32
	mref num,1
	mref id,2
	mref ftype,3
	string=st
	getptr pstring,string
	strlen length,string
	menuiteminfo=44,0x0013,ftype,0x0000,id,0,0,0,0,pstring,length
	;0x0013はMIIM_STATE or MIIM_ID or MIIM_TYPE
	;0x0000はMFS_ENABLED
	getptr pmenuiteminfo,menuiteminfo
	prm=hPopupMenu.num,-1,0x0400,pmenuiteminfo
	dllproc "InsertMenuItemA",prm,4,D_USER
	dllproc "DrawMenuBar",bmscr.13,1,D_USER
return

#deffunc checkitem int,int,int
	mref popupnum,0
	mref menunum,1
	mref checkflg,2
	if checkflg : checkmark=0x0008 : else : checkmark=0x0000
	;0x0008はMFS_CHECKED、0x0000はMFS_UNCHECKED
	menuiteminfo=44,0x0009,0,checkmark,0,0,0,0,0,0,0
	;0x0009はMIIM_STATE or MIIM_CHECKMARKS
	getptr pmenuiteminfo,menuiteminfo
	prm=hPopupMenu.popupnum,menunum,0x0400,pmenuiteminfo
	dllproc "SetMenuItemInfoA",prm,4,D_USER
	dllproc "DrawMenuBar",bmscr.13,1,D_USER
return

#deffunc getcmd val
	mref cmd,16
	getptr pmsg,msg
	prm=pmsg,0,0,0,0
	dllproc "PeekMessageA",prm,5,D_USER
	if dllret@=0 : cmd=0 : return
	dllproc "GetMessageA",prm,4,D_USER
	if (dllret@=0)|(dllret@=-1) : end
	if msg.1=0x0111 : cmd=msg.2 & 0xffff : else : cmd=0
	dllproc "TranslateMessage",pmsg,1,D_USER
	dllproc "DispatchMessageA",pmsg,1,D_USER
return

#global

	setmenu
	setpopup "項目1",10
	additem "アイテム1",0,11
	additem "アイテム2",0,12
	additem "",0,,$800
	additem "アイテム3",0,13
	setpopup "項目2",20
	additem "アイテム4",1,21
	additem "アイテム5",1,22
	additem "アイテム6",1,23
	checkflg=0	;チェックマーク用フラグ

*getmessage
	getcmd cmd
	if cmd=11 : dialog "アイテム1が選択されました"
	if cmd=12 : dialog "アイテム2が選択されました"
	if cmd=13 : dialog "アイテム3が選択されました"
	if cmd=21 : dialog "アイテム4が選択されました"
	if cmd=22 : dialog "アイテム5が選択されました"
	if cmd=23 {
		dialog "アイテム6が選択されました\nアイテム6のチェックマークを変更します"
		checkflg=checkflg^1	;0ならば1に、1ならば0に設定
		checkitem 1,2,checkflg
	}
	goto *getmessage

まずAPI関数の説明をします。

CreateMenu
メニューを作成します。返り値にメニューハンドルがセットされます。

SetMenu ウィンドウハンドル,メニューハンドル
指定したウィンドウにメニューを設定します。

DrawMenuBar ウィンドウハンドル
ウィンドウに貼り付けられているメニューを再描画します。

CreatePopupMenu
ポップアップメニューを作成します。返り値にメニューハンドルがセットされます。

InsertMenuItemA メニューハンドル,項目の位置,項目の位置のモード,MENUITEMINFO構造体ポインタ
メニューに項目を追加します。項目の位置のモードを0x0000(MF_BYCOMMAND)で第2引数の値はメニュー内のIDで識別。 0x0400(MF_BYPOSITION)で第2引数の値はメニュー内の相対位置と判断します。

MENUITEMINFO構造体
.0 構造体のサイズ(44)
.1 利用メンバ設定
※MIIM_STATEはfStateメンバ、MIIM_IDはwIDメンバ、MIIM_TYPEはfTypeメンバ、MIIM_SUBMENUはhSubMenuメンバ、MIIM_CHECKMARKSはhbmpChecked、hbmpUnchecked メンバが有効になります。
.2 メニュー項目のタイプの設定
※MFT_STRINGはアイテムが文字列である事を示し、MFT_SEPARATORはセパレータである事を示します。
.3 メニュー項目の状態の設定
※MFS_ENABLEDは項目を有効にします。MFS_CHECKEDは項目をチェックし、MFS_UNCHECKEDはチェックを解除します。
.4 メニューのID
.5 サブメニューハンドル
.6 チェックマークに使用するビットマップハンドル
.7 チェックしていないときに表示するビットマップハンドル
.8 アプリケーション定義の32ビット値
.9 文字列ポインタやビットマップハンドルなど、表示するもの関係
.10 表示するものが文字列の場合、文字列のバイト数

SetMenuItemInfoA メニューハンドル,項目の位置,項目の位置のモード,MENUITEMINFO構造体ポインタ
メニュー項目の情報を設定します。パラメータはInsertMenuItem関数と同じです。

定義した命令は、

setmenu
ウィンドウにメニューをセットします。メニュー本体を作成しているので他の命令の前に必ず入れてください。

setpopup 表示する文字列,コマンドID
メインメニューにポップアップメニューを追加します。additemの前にいれてください。

additem 表示する文字列,ポップアップメニューのNo,コマンドID,アイテムのタイプ
ポップアップメニューに項目を追加します。第4引数に0x0800(MFT_SEPARATOR)を指定すると、項目はセパレータ(区切り線)になります。通常は省略して構いません。
アイテムNoは0から順に増えていきます。

checkitem ポップアップメニューのNo,アイテムのNo,チェックフラグ
指定したアイテムのチェックマークの設定をします。
チェックフラグに1を指定するとそのアイテムをチェック、0を指定するとチェックをはずします。

getcmd コマンドIDを代入する変数
メニューがクリックされたときにそのメニューのIDを取得します。ループにいれてお使いください。
メッセージに関してはメッセージ取得で…。

という感じでしょうか…。
できるだけExforce.dllに似せてみたんだけど全然だね。機能も少なすぎるし。
とりあえず基本的な流れは、

1.CreateMenu関数でメニューを作成
2.SetMenu関数でウィンドウに割り当て
3.CreatePopupMenu関数でポップアップメニューを作成
4.InsertMenuItemA関数でメインメニューにアイテムを挿入
5.InsertMenuItemA関数でポップアップメニューにアイテムを挿入
※メニューの更新があったところにDrawMenuBar関数も入れる

という感じで大丈夫です。ここでの命令で言えば、

setmenu→setpopup→additem

だけで出来ます。

getcmd命令は自力でメッセージを取得し、WM_COMMANDが送られてきたときのwPramからIDを取り出しています。
ちなみにgetcmdのあるループではwait・awaitがいりません。

低機能ですが、標準命令でメニューバーが扱えるようになればいろいろと使い道があるんじゃないでしょうか?
ちなみにツールバーはメッセージの取得が出来ません。ツールバーなどのコントロールのメッセージは直接ウィンドウプロシージャに送られるため GetMessageAでは受け取れません。よって今の段階でツールバーを使う事はAPIだけでは不可能です。


戻る