コモンコントロールの基礎

コントロールとは

HSPのbutton命令やmesbox命令、combox命令などを使って作成することができるオブジェクト(HSPオブジェクト)は、Windowsでは総称して『コントロール』と呼ばれます。これは、Windowsによって提供されているウィンドウクラスのセットのことで、簡単に言ってしまうと、Windowsより提供されている、独自の機能を持ったウィンドウといったところです。

Windowsが提供しているコントロールには、コントロールを提供しているDLLによっていくつかのグループに分類され、それぞれのグループで次のような種類のコントロールが存在します。


上記のコントロールのうち、user32.dllが提供する標準コントロールのいくつかは、HSPの標準命令で作成することができるようになっています。

コントロール HSP命令
ボタンコントロール button命令
エディットコントロール mesbox命令およびinput命令
コンボボックス combox命令
リストボックス listbox命令

コモンコントロール

上で新なしたコントロールの中で、これから作成していこうとしているコントロールは、ツールバーやステータスバー、ツリービューなどといったコモンコントロールです。今回は、コモンコントロールについてを簡単に紹介しておきます。また、今後コモンコントロールを使っていく上で、必要となることも、あわせて述べていくことにしましょう。

コモンコントロールは、Windowsのコモンコントロールライブラリによって提供されるウィンドウセットです。上でも記述しましたが、コモンコントロールには以下のようなものがあります。

これらのコントロールがコモンコントロールライブラリにより提供されていると述べましたが、このライブラリの正体はcomctl32.dllです。このDLLによって提供されている関数を呼び出していくことで、コモンコントロールを作成することができるのです。

コモンコントロールライブラリは、コモンコントロール以外にも、それらをサポートするための機能として、イメージリストと呼ばれる機能も提供しています。イメージリストは、主にアイコンなどのイメージを使用するコモンコントロールで使われます。

コモンコントロールのバージョン依存性

コモンコントロールを提供しているcomctl32.dllのバージョンアップに伴い、いろいろな機能が追加されてきています。こういった、あとから追加された機能を使用する場合、古いバージョンのDLLがインストールされているマシンでは、そのソフトウェアが正常に動かなくなってしまう可能性があります。新しい機能を使用したソフトウェアを公開する場合には、その動作環境をはっきりと明記しておきましょう。

comctl32.dllのバージョンとその動作環境の対応は、以下のようになります。

Ver. 動作環境
4.0 Windows 95 / NT 4.0 がインストールされた環境
4.70 Internet Explorer 3.x がインストールされた環境
4.71 Internet Explorer 4.0 がインストールされた環境
4.72 Internet Explorer 4.01 / Windows 98 がインストールされた環境
5.8 Internet Explorer 5 がインストールされた環境
5.81 Windows Me / 2000 がインストールされた環境
6.0 Windows XP がインストールされた環境

コモンコントロール作成の準備

DLLの初期化とウィンドウクラス

コモンコントロールを使うためには、まずコモンコントロールライブラリを初期化しなければいけません。これには、comctl32.dllが提供するInitCommonControls関数またはInitCommonControlsEx関数を呼び出す必要があります。

これらの関数は、コモンコントロールを使いますよー、ということをcomctl32.dllに伝えて、準備をするためのものです。詳しく言うと、この関数は、それぞれのコモンコントロールに対するウィンドウクラスをWindowsシステムに登録します。

ウィンドウクラスというのは、ウィンドウに関するデータと、ウィンドウの動作を決める関数(ウィンドウプロシージャ)を定義した情報を含めた、いわばウィンドウの雛形、あるいは設計図のようなものです。これから作成されるウィンドウがどのようなスタイルを持っていて、ウィンドウメッセージにどのように反応するウィンドウプロシージャを持っているのか、といった情報をウィンドウクラスという形で保持してあるのです。ウィンドウクラスに関する情報は、Windowsの管理するメモリ上のデータベースに格納されます。Windowsは、この情報を元にして、ウィンドウの作成や表示、メッセージの送信などを行うのです。

通常、アプリケーションがウィンドウを作成する際には、ウィンドウ情報を含むウィンドウクラスを登録しなければいけません。HSPのウィンドウも例外ではなく、HSP内部ではウィンドウクラスを登録してウィンドウを作成しているのです。一方、Windows標準コントロールは、Windowsの提供するウィンドウとしてはじめからウィンドウクラスが登録されていて、アプリケーションはそれを自由に使うことができます。また、コモンコントロールの場合には、初期化関数を呼び出すことによって、自動的にウィンドウクラスが登録されるようになっています。

InitCommonControlsによる初期化

InitCommonControls関数は、次のように定義されています。

void InitCommonControls(VOID);

InitCommonControls関数は、コモンコントロールライブラリが持つすべての種類のコモンコントロールのウィンドウクラスを登録します。どの種類のコントロールを、いくつ作成する場合であっても、プログラムの最初に一度だけ呼び出せばOKです。1つのプログラム中でInitCommonControls関数を2回以上呼び出した場合、2回目以降の呼び出しでは何もせずにすぐに制御が返ります。

InitCommonControlsExによる初期化

コモンコントロール初期化用の関数にはInitCommonControls関数の他にもう1つあります。それは、comctl32.dllのバージョンアップに伴って新しく追加されたInitCommonControlsEx関数です。comctl32.dllのバージョン4.70以降で使用することができます。

BOOL InitCommonControlsEx(
    LPINITCOMMONCONTROLSEX pInitCtrls
);

pInitCtrlsパラメータには、INITCOMMONCONTROLSEX構造体のアドレスを指定します。この構造体は以下のように定義されているものです。

typedef struct tagINITCOMMONCONTROLSEX {
    DWORD dwSize;    // structure size
    DWORD dwICC;     // control class
} INITCOMMONCONTROLSEX, *LPINITCOMMONCONTROLSEX;

この構造体のdwSizeメンバには、INITCOMMONCONTROLSEX構造体のサイズをバイト単位で指定します。この構造体のサイズは8バイトであるので、このメンバに8を格納しておきます。

dwICCメンバには、使用するコントロールの種類を表す値を格納しておきます。この値は、以下に示す値を組み合わて指定することができます。

定数名コントロールの種類
ICC_LISTVIEW_CLASSES 0x00000001 リストビュー
ヘッダーコントロール
ICC_TREEVIEW_CLASSES 0x00000002 ツリービュー
ツールチップ
ICC_BAR_CLASSES 0x00000004 ツールバー
ステータスバー
トラックバー
ツールチップ
ICC_TAB_CLASSES 0x00000008 タブコントロール
ツールチップ
ICC_UPDOWN_CLASS 0x00000010 アップダウンコントロール
ICC_PROGRESS_CLASS 0x00000020 プログレスバー
ICC_HOTKEY_CLASS 0x00000040 ホットキーコントロール
ICC_ANIMATE_CLASS 0x00000080 アニメートコントロール
ICC_WIN95_CLASSES 0x000000FF アニメートコントロール
ヘッダーコントロール
ホットキーコントロール
リストビュー
プログレスバー
ステータスバー
タブコントロール
ツールチップ
ツールバー
トラックバー
ツリービュー
アップダウンコントロール
ICC_DATE_CLASSES 0x00000100 DTPコントロール
ICC_USEREX_CLASSES 0x00000200 拡張コンボボックス
ICC_COOL_CLASSES 0x00000400 レバーコントロール
ICC_INTERNET_CLASSES 0x00000800 IPアドレスコントロール

InitCommonControlsEx関数では、上記のdwICCメンバで指定されたコントロールのウィンドウクラスだけを登録するようになっています。必要なコントロールクラスのみをロードすることができるので、InitCommonControls関数を使ってすべてのコモンコントロールクラスを登録するよりも、使用されるメモリを節約し、初期化の時間を短縮することができるということです。

InitCommonControlsEx関数が存在するかを調べる

上述の通り、InitCommonControlsEx関数は新しく追加された関数であるため、バージョン依存性があります。この関数はcomctl32.dllのバージョン4.70以降が提供しており、Windows 98/MeやWindows 2000/XPがインストールされている環境、またはInternet Explorer 3.0以降がインストールされている環境で呼び出すことができます。

これよりも古いバージョンのDLLがインストールされている環境ではInitCommonControlsEx関数を使用することができないので、そういった環境でも動くプログラムを作成するには、常にInitCommonControls関数を呼び出すようにすればよいでしょう。

もしくは、DLLにInitCommonControlsEx関数が存在するかどうかを調べて、存在する場合のみ、その関数を呼び出すようにすることもできます。

DLLにその関数が存在するかどうかを、HSP標準関数のvarptrを使って調べることができます。通常、varptr関数は変数のアドレスを取得するために使用するものですが、変数名の変わりに関数名を指定すると、関数のアドレスを取得することができるようになっています。そして、DLLにその関数が含まれていない場合には0になるのです。

たとえば、ツールバーやステータスバーなどを作成したい場合には、次のようにすることができます。

#uselib "comctl32.dll"
#func InitCommonControls   "InitCommonControls"
#func InitCommonControlsEx "InitCommonControlsEx" int

#const ICC_BAR_CLASSES      0x00000004

; comctl32.dll に InitCommonControlsEx() があるかどうか
if ( varptr(InitCommonControlsEx) != 0 ) {
    icc = 8, ICC_BAR_CLASSES
    InitCommonControlsEx varptr(icc)
} else {
    InitCommonControls
}

一方、バージョン4.70よりも古いDLLでは提供されていない種類のコモンコントロールを使いたい場合には、はじめからInitCommonControlsExを使用するようにするといいでしょう。たとえば、IPアドレスコントロールはバージョン4.71以降で使用することができるコントロールですが、これを使用するには以下のようにすることができるでしょう。

#uselib "comctl32.dll"
#func InitCommonControlsEx "InitCommonControlsEx" int

#const ICC_INTERNET_CLASSES     0x00000004

gosub *InitComctlLib    ; 初期化ルーチンにジャンプ
; (中略)
stop

*InitComctlLib
; ======== IPアドレスコントロール用に初期化 ========
; comctl32.dll に InitCommonControlsEx() があるかどうか
if ( varptr(InitCommonControlsEx) != 0 ) {
    icc = 8, ICC_INTERNET_CLASSES
    InitCommonControlsEx varptr(icc)
    if stat != 0 : return
}
; 初期化エラー
dialog "古いバージョンのcomctl32.dllです", 1, "初期化エラー"
end

コモンコントロールの作成

ウィンドウの作成 - CreateWindow関数

コモンコントロールの実体は、Windowsから提供されているウィンドウです。したがって、ウィンドウ作成用のAPI関数であるCreateWindow関数を使って作成することができます。この関数はuser32.dllによって提供されており、次のように定義されています。

HWND CreateWindowA(
    PCTSTR pszClassName,  // window class
    PCTSTR pszWindowName, // window name
    DWORD  dwStyle,       // window style
    int    x,             // x-position
    int    y,             // y-position
    int    nWidth,        // width
    int    nHeight,       // height
    HWND   hWndParent,    // parent window
    HMENU  hMenu,         // menu handle or control ID
    HINSTANCE hInstance,  // instance handle
    PVOID  pParam         // CREATESTRUCT structure
);

最初のpszClassNameパラメータには、ウィンドウクラスの名前を表す文字列へのポインタを指定します。ウィンドウクラスはそれぞれ名前が付けられていて、その名前によって識別されます。通常、自身でウィンドウクラスを登録して、そのウィンドウクラスを指定する場合には、クラスの登録時に指定した名前を指定します。ただし、Windowsが提供する標準コントロールについてはすでにそのウィンドウクラスが登録されていますし、コモンコントロールは上記のライブラリ初期化によってウィンドウクラスが自動的に登録されているので、Windowsが定める定義済みのウィンドウクラス名を指定することで、それぞれのコントロールを作成することができます。コモンコントロールのウィンドウクラス名は次のようになっています。ここでは、クラス名に大文字と小文字が混ざって書かれていますが、実際には大文字・小文字は区別されません。

ウィンドウクラス名 作成されるコモンコントロール
"SysAnimate32" アニメートコントロール
"msctls_hotkey32" ホットキーコントロール
"msctls_progress32" プログレスバー
"msctls_statusbar32" ステータスバー
"ToolbarWindow32" ツールバー
"tooltips_class32" ツールチップコントロール
"msctls_trackbar32" トラックバー
"msctls_updown32" アップダウンコントロール
"ComboBoxEx32" 拡張コンボボックス
"SysHeader32" ヘッダーコントロール
"SysListView32" リストビュー
"SysTabControl32" タブコントロール
"SysTreeView32" ツリービュー
"SysDateTimePick32" Ver. 4.70以降: DTPコントロール
"SysMonthCal32" Ver. 4.70以降: 月間カレンダーコントロール
"ReBarWindow32" Ver. 4.70以降: レバーコントロール
"SysIPAddress32" Ver. 4.71以降: IPアドレスコントロール
"SysPager" Ver. 4.71以降: ページャーコントロール

次のpszWindowNameパラメータには、ウィンドウに割り当てる文字列を指定します。通常、タイトルバーが表示される種類のウィンドウの場合には、ここで指定された文字列がウィンドウのタイトルバーに表示されます。また、コントロールを作成する場合には、コントロールの種類によって異なります。たとえば、ボタンやエディットコントロールの場合には、コントロール内に表示される文字列を指定します。

次のdwStyleパラメータには、ウィンドウスタイルを指定します。これらは、すべてのウィンドウに共通なスタイルもありますが、作成するコントロールによって指定できる値が変わってくるスタイルもあります。すべてのウィンドウに共通のウィンドウスタイルには次のものがあります。複数のスタイルはOR演算子で組み合わせて指定します。とくに指定する必要ではありません。

定数名 意味
WS_OVERLAPPED 0x00000000 オーバーラップスタイル
WS_POPUP 0x80000000 ポップアップ
WS_CHILD 0x40000000 子ウィンドウ
WS_MINIMIZE 0x20000000 最小化状態
WS_VISIBLE 0x10000000 可視状態
WS_DISABLED 0x08000000 無効状態
WS_CLIPSIBLINGS 0x04000000 兄弟ウィンドウのクリッピング
WS_CLIPCHILDREN 0x02000000 子ウィンドウのクリッピング
WS_CAPTION 0x00C00000 タイトルバー付き
WS_BORDER 0x00800000 境界線付き
WS_DLGFRAME 0x00400000 ダイアログボックス用境界
WS_VSCROLL 0x00200000 垂直スクロールバー
WS_HSCROLL 0x00100000 水平スクロールバー
WS_SYSMENU 0x00080000 ウィンドウメニュー付き
WS_THICKFRAME 0x00040000 サイズ変更境界線
WS_GROUP 0x00020000 グループ開始ウィンドウ
WS_TABSTOP 0x00010000 [Tab]キーで移動
WS_MINIMIZEBOX 0x00020000 最小化ボタン付き
WS_MAXIMIZEBOX 0x00010000 最大化ボタン付き

これ以外にも、コントロールの種類によって、標準コントロールごとに固有のスタイルコモンコントロールごとに固有のスタイルが存在します。これらについては、必要に応じて説明していきます。

ただし、どのコントロールを作成する場合も、あるウィンドウの子ウィンドウにならなければならないので、WS_CHILDスタイルを指定しておかなければなりません。また、表示された状態にするにはWS_VISIBLEスタイルも組み合わせて指定されていなければなりません。

以降に続くx, y, nWidth, nHeightの各パラメータにはウィンドウの位置座標とサイズを指定します。ウィンドウスタイルに子ウィンドウスタイル(WS_CHILD)を含んでいる場合には、ウィンドウの位置はクライアント座標で指定することになります。

8番目のhWndParentパラメータには、作成されるウィンドウを所有するオーナーウィンドウのハンドルを指定します。新しいウィンドウが子ウィンドウスタイルを持つ場合には、このウィンドウは親ウィンドウと呼ばれます。コントロールは子ウィンドウとして作成しなければならないので、親ウィンドウを指定しなければいけません。HSPでは、システム変数hwndを指定することで、現在の描画中ウィンドウを指定することができます。

9番目のhMenuパラメータには、ウィンドウがポップアップスタイルかオーバーラップスタイルを持つ場合にはメニューハンドルを指定します。ウィンドウが子ウィンドウスタイルの場合には、子ウィンドウID(コントロールID)と呼ばれる識別値を指定します。

次のhInstanceパラメータには、インスタンスハンドルと呼ばれる値を指定します。インスタンスとは、メモリ上にロードされたプログラムの実体のことを指します。このインスタンスはハンドルによって識別されます。インスタンスハンドルは、HSPの場合には、システム変数hinstanceが実行中プログラムのインスタンスハンドルになります。

11番目のlpParamパラメータには通常使用されないもので、ここでは0 (NULL) を指定しておきます。

CreateWindow関数は、戻り値として、作成されたコントロールのウィンドウハンドルを返します。エラーが発生してウィンドウを作成できなかった場合には0 (NULL) が返ります。コントロールを作成した後、コントロールに対して操作を行う場合には、このハンドルが必要になります。

拡張スタイルの指定 - CreateWindowEx関数

ウィンドウを作成する関数には、もう1つ、CreateWindowEx関数が存在します。

HWND CreateWindowExA(
    DWORD  dwExStyle,     // extended window style
    PCTSTR pszClassName,  // window class
    PCTSTR pszWindowName, // window name
    DWORD  dwStyle,       // window style
    int    x,             // x-position
    int    y,             // y-position
    int    nWidth,        // width
    int    nHeight,       // height
    HWND   hWndParent,    // parent window
    HMENU  hMenu,         // menu handle or control ID
    HINSTANCE hInstance,  // instance handle
    PVOID  pParam         // CREATESTRUCT structure
);

この関数がCreateWindow関数と異なるのは、最初のdwExStyleパラメータで、拡張ウィンドウスタイルを指定することができるようになっている点です。これ以外のパラメータについては、CreateWindow関数とまったく同じです。

一般的なコントロールでは、あえて特定の拡張ウィンドウスタイルを指定する必要はないと思います。この関数のdwExStyleパラメータに0を指定するか、またはCreateWindow関数の方を使用すればよいでしょう。

コントロール作成命令 winobj

さて、HSP3では、このCreateWindowEx関数を呼び出すラッパー命令であるwinobj命令が標準で提供されています。

winobj "s1", "s2", n3, n4, n5, n6, n7, n8
s1 : ウィンドウクラス名
s2 : ウィンドウテキスト
n3 : 拡張ウィンドウスタイル
n4 : ウィンドウスタイル
n5 : Xサイズ(省略時はobjsize指定値)
n6 : Yサイズ(省略時はobjsize指定値)
n7 : 子ウィンドウID(コントロールID)

winobj命令は、現在の描画先ウィンドウをオーナーウィンドウ(または親ウィンドウ)とするウィンドウを作成します。作成されたウィンドウは、他のHSPオブジェクトと同等に管理されます。すなわち、作成された順に0, 1, 2,... のオブジェクトIDが割り当てられていくことになります(HSPのオブジェクトIDとCreateWindowEx関数の子ウィンドウIDとは関連性はありません)。このHSPオブジェクトIDは、winobj命令実行直後にシステム変数statにも格納されます。ウィンドウハンドルについては、HSP標準関数のobjinfo関数またはobjinfo_hwndマクロにオブジェクトIDを渡すことで取得することができます。すなわち、

; オブジェクトID = 0 のコントロールのハンドルを取得
hControl = objinfo(0,2)

または、

; オブジェクトID = 0 のコントロールのハンドルを取得
hControl = objinfo_hwnd(0)

のようにします。

表示・非表示のコントロール

ところで、初期状態で非表示のコントロールを作成するために、CreateWindow関数やCreateWindowEx関数(またはwinobj命令)のウィンドウスタイルにWS_VISIBLEスタイルを指定しないでコントロールを作成することができます。この場合は、コントロールが作成された時点では表示されていないので、コントロールを後から表示させる必要があります。非表示状態のコントロールを表示させるには、user32.dllShowWindow関数を呼び出します。

BOOL ShowWindow(
    HWND hWnd,     // window handle
    int  nCmdShow  // show state
);

この関数は、ウィンドウの表示状態を変更するためのものです。コントロールはすべてウィンドウであるので、この関数で表示状態を変更することができます。

hWndパラメータには、表示状態を変更するウィンドウのハンドルを指定します。非表示状態のコントロールを表示させるためには、このパラメータにコントロールのハンドルを指定する必要があります。

nCmdShowパラメータには、ウィンドウの表示状態を表す値を指定します。非表示状態のコントロールを表示させるには、このパラメータに1 (SW_SHOWNORMAL) を指定します。逆に、表示されていたコントロールを非表示状態にしたい場合には0 (SW_HIDE) を指定します。

コモンコントロールに送るメッセージ

作成したコモンコントロールを操作するためには、専用のAPI関数を使うよりもむしろ、そのコントロールにメッセージを送るということが行なわれます。アプリケーションがSendMessage関数でコントロールにある特定のメッセージを送ると、そのメッセージを受け取ったコントロールは、そのメッセージに応じた処理を行なって、必要ならば戻り値も返します。メッセージの送信によって、まるでAPI関数を使っているかのようにコントロールを操作することができます。このとき、引数を同じ役割を果たすのは、メッセージとともに送られるwParamパラメータとlParamパラメータです。HSP標準命令のobjsend命令でHSPオブジェクトにメッセージを送るといろいろな操作ができますが、あれとまったく同じです。

どんなメッセージコードを送ればどんな操作ができるのかはコントロールの種類によって異なります。

llmodモジュールを使ったメッセージの送信には、モジュール定義命令のsendmsg命令を使うことができます。このページでも、メッセージの送信にはこの命令を頻繁に使用していきます。

ウィンドウメッセージの送信およびsendmsg命令については、『メッセージの送信とポスト』の節を参照してください。

ウィンドウメッセージを発生させるにはSendMessage関数によるメッセージ送信と、PostMessage関数によるメッセージのポストがありましたが、コントロールに対してのメッセージはすべてSendMessage 関数を使います。

コモンコントロールから送られてくるWM_NOTIFYメッセージと通知コード

通知メッセージ

コモンコントロールは、何らかのイベントが起こったとき、例えばコントロール内にユーザーからの入力があったときに、たいていの場合そのことを通知メッセージ という形で親ウィンドウに知らせます。アプリケーションは、この通知メッセージに応じて、どんな処理をするかを決定していくことができます。ツールバーやステータスバーなどを使う場合にはあまり必要のないことなのですが、ツリービューやリストビューなどでは使うことがしばしばあるので、ここで述べておくことにします。

コモンコントロールからの通知メッセージはWM_NOTIFYメッセージの形で送られてきます。このメッセージコードは次のように定義されています。

#define  WM_NOTIFY    0x004E

このメッセージのwParamパラメータはコモンコントロールのIDを、lParamパラメータはNMHDR構造体のアドレス、もしくはNMHDR構造体を第1メンバに持つ構造体のアドレスを示しています。この構造体には、通知メッセージを含む、コモンコントロールで発生したイベントの情報が含まれています。

NMHDR構造体は以下のように定義されています。

typedef struct tagNMHDR {
    HWND hwndFrom;   // control handle
    UINT idFrom;     // control ID
    UINT code;       // notification code
} NMHDR;

この構造体のhwndFromメンバはコントロールのウィンドウハンドルを、idFromメンバはコントロールIDを、そしてcodeメンバは通知コードを表しています。この構造体を変数nmhdrにとってみたとき、

nmhdr(0) = (コントロールのハンドル)
nmhdr(1) = (コントロールID)
nmhdr(2) = (通知コード)

というようになります。

ところで、「NMHDR構造体を第1メンバに持つ構造体」というのがどういった構造体であるのかは、やや分かりにくいかもしれませんね。簡単に説明すると、先頭にNMHDR構造体のデータが丸ごと入っていると考えます。例えば、多くのコモンコントロールは、コントロールにキーボードフォーカスがあるときにキーが押されると、通知メッセージNM_KEYDOWN (コード-15)を送るのですが、この場合はWM_NOTIFY メッセージのlParamパラメータはNMKEYという構造体のアドレスとなっています。

typedef struct tagNMKEY {
    NMHDR hdr;      // NMHDR structure
    UINT  nVKey;    // virtual key-code
    UINT  uFlags;   // key-state flag
} NMKEY, FAR *LPNMKEY;

このNMKEY構造体は、その第1メンバにNMHDR構造体を持ちます。ここで、この構造体を変数nmkeyにとってみたとき、

nmkey(0) = (コントロールのハンドル)
nmkey(1) = (コントロールID)
nmkey(2) = -15 ;(通知コード NM_KEYDOWN
nmkey(3) = (仮想キーコード)
nmkey(4) = (キー状態を表すフラグ)

というようになります。こうして考えるとだいたい仕組みが分かると思います。どの構造体が送られてくるかは、コモンコントロールの種類と、そのコントロールから送られてくる通知メッセージの種類によって異なります。

HSPにおける通知メッセージの取得

HSP3では、ほかのメッセージと同様にoncmd命令でWM_NOTIFYメッセージの処理を登録することによって、通知メッセージに対する処理を行うことができるようになります。

#define WM_NOTIFY     0x004E
oncmd gosub *OnNotify, WM_NOTIFY

通知メッセージを受け取ると指定ラベルにジャンプします。そのとき、lParamパラメータ(システム変数lparamの値)がNMHDR構造体のアドレスになっているので、そのアドレスがさす構造体の中を参照するため、dupptr命令を使って、その構造体領域をHSPの変数に割り当てます。このとき、構造体サイズの12を指定します。

*OnNoify
dupptr nmhdr, lparam, 12    ; NMHDR 構造体の割り当て
hCondrol  = nmhdr(0)        ; コントロールのハンドル
idControl = nmhdr(1)        ; コントロールID
code      = nmhdr(2)        ; 通知コード

; (ここでメッセージに対する処理を行う)

return 0

通知コードによっては、lParamパラメータに渡されるポインタが、NMHDR構造体ではなく、NMHDR構造体を第1メンバとするような構造体になることがあります。そのような場合には、通知コードを判断した後、再びdupptr命令で割り当てます。

*OnNoify
dupptr nmhdr, lparam, 12    ; NMHDR 構造体の割り当て
hCondrol  = nmhdr(0)        ; コントロールのハンドル
idControl = nmhdr(1)        ; コントロールID
code      = nmhdr(2)        ; 通知コード
if code == NM_KEYDOWN {
    dupptr nmkey, lparam, 20
    vkey    = nmkey(3)      ; (仮想キーコード)
    keyflag = nmkey(4)      ; (キー状態を表すフラグ)
    ; (ここでメッセージに対する処理を行う)
}
return 0

コモンコントロールから送られてくるWM_COMMANDメッセージと通知コード

コモンコントロールは、コントロール内で起こったイベントを親ウィンドウに知らせるのに、WM_COMMANDメッセージを使うことがあります。例えば、ツールバーは、ツールボタンが押されたときにそのボタンのアイテムIDをWM_COMMANDメッセージのパラメータに含ませて親ウィンドウに送るのです。親ウィンドウはそのメッセージを取得することで、ツールボタンが押されたことを知ることができます。このとき、WM_COMMANDメッセージのlParamパラメータには、コントロールのハンドルが指定されます。

これについてはここでは詳しく述べません。必要になったときにそれぞれ述べていくことにします。


今回はかなり長くなってしまいましたが、コモンコントロールを扱う上での前置きとしてはこのぐらいでしょう。次回からは何か具体的なコモンコントロールを使っていきたいと思います。