[level4] [ウィンドウ] [ HWND ] [ HINSTANCE ] [ MSG構造体] [Win32 データ型] [コールバック関数]
MSG構造体
自作関数 MyPreTranslateMessage()
MSG 構造体は Windows API(Win32 API) の構造体です。
MSG 構造体そのものは、説明を要するほどのものではありませんが、数少ない MSG 構造体に触れる例の中で、スケルトンコードの段階で付け加えてもよいと思われる、すこし便利な一例を紹介します。
MSG 構造体は、winuser.h で、typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; ... 略 ... } MSG, ...略... ;
のように定義されています。述べておいてなんですが、この定義はたいして重要でありません。
hwnd, message, wParam, lParam のそれぞれは、ウィンドウプロシージャのそれに対応するものです。
MFC などのコード例では、BOOL xxx::PreTranslateMessage (MSG *) ; という便利な 「メンバ関数」 が使えます。この関数は、メッセージを OS またはウィンドウプロシージャに流すまえに、メッセージの内容を吟味するために使え、場合によってはウィンドウプロシージャに渡さずに奪ってしまうこともできます。
この関数は、各ラップされたクラスのメンバ関数なので、まったく同じことを真似ることはできませんが、「同質」 の処理としては負けずに以下のようにすることができます。
まず、メッセージループの部分を、
while ( 0 != ( ret = GetMessage( &msg, NULL, 0, 0 ))) { if ( ret != -1 ) { if( MyPreTranslateMessage( &msg )) { TranslateMessage( &msg ) ; DispatchMessage( &msg ) ; } } }
のようにしてしまいましょう。
この MyPreTranslateMessage() は、
BOOL MyPreTranslateMessage(MSG *pmsg) ;
// プロトタイプ宣言BOOL MyPreTranslateMessage(MSG *pmsg) // 関数の実体 : 位置は適当に。 { // ・・・とりあえずはなにもせず、TRUE だけ返す ・・・ return TRUE ; } WNDPROC WndProc( HWND ........ ) { ......
.....
この関数はいまのところ、常に TRUE を返します。
そして、メッセージループのところに目を向けて流れを追えばわかるように、メッセージループの一連の処理に変化はありません。だから、いつもスケルトンコードにこのように追加しておいても大丈夫です。
ここまでやれば、
以下はもうお好みです。
もう理解できた場合はいいですが、さらに、
BOOL MyPreTranslateMessage(MSG *pmsg) // 関数の実体 : 位置は適当に。 { HWND hWnd = pmsg->hwnd ; WPARAM wParam = pmsg->wParam ; LPARAM lParam = pmsg->lParam ; UINT msg = pmsg->message ; // ・・・とりあえずはなにもせず、TRUE だけ返す ・・・ return TRUE ; }
のようにするとわかりやすいです。
もちろんこれら関数内のローカル変数を設ける訳は、具体的にやりたいことのためです。
関数の意味は、Pre-Translate だから、メッセージ解釈の前、ということなのです。例えば子ウィンドウ(コントロール) のメッセージは親ウィンドウに知らされますが、そのようにメッセージが渡される前にやりたいことを、この関数内になんでも記述してしまいます。
[具体的な利用方法]
このようにやると、メッセージループの部分が少し膨れてしうことは否めませんが、どうしてもというときに使用します。たとえば非常に細かく、コントロールへのキー入力を処理する場合などに応用できます。細かくはいいませんが、hWnd は、メッセージがあるたびに、異なるコントロールアイテムやウィンドウの ウィンドウハンドルです。
※ UINT 型は符号なし (Unsigned) 32 ビットの値。MSG 型 (構造体) で MSG msg ; のような識別名を使う場合があるので "非常に混乱" します。ここでの msg は、単なる数字なのです。
また、実はまだ使い道があります。
"モードレスダイアログボックス" で、[TAB] キーによるコントロール間のフォーカスの移動を有効にしたいときは、 IsDialogMessage() 関数をメッセージループに「組み込」 むことで可能になりますが、どうせ組み込むなら、この MyPreTranslate() 関数を使えばスマートに記述できます。すなわち
BOOL MyPreTranslateMessage(MSG *pmsg) // 関数の実体 : 位置は適当に。 { // HWND hWnd = pmsg->hwnd ; WPARAM wParam = pmsg->wParam ; LPARAM lParam = pmsg->lParam ; UINT msg = pmsg->message ; if( IsDialogMessage( hDlg , pmsg )) return FALSE ; // if( TranslateAccelerator( pmsg->hwnd /*一時的*/ , hAccel /*一時的*/ , pmsg )) return FALSE ; // アクセラレータを使いたいならこのようにしよう。 return TRUE ; }
こうすると、FALSE が返される場合は結果としてメッセージループのメッセージを奪っていることにはなりますが、IsDialogMessage() 関数のときはむしろそれが「お約束ごと」 ( メッセージループの TranslateMessage() 関数に処理させてはいけない ) だからこれでいいのです。
hDlg は、グローバル変数にとっておいた"モードレスダイアログボックス"のハンドルです。この、モードレスダイアログの [TAB] キーのことをやる場合は、(ダイアログボックスの)ハンドルをグローバル変数にとっておくほかにはやりようがありません。
[TAB] キーのことをやるときに、ダイアログボックスが複数あるのであれば、前処理を関数にしておかないとシンプルなメッセージループがみるみる汚くなってしまうから、こうせざるをえないはずです。そして、一つしかダイアログボックスが使っていなくても、のちに二つ以上に増えたとき、そのときになって、いちいちこのような関数に移行するのもおそらくは手間だと思うのです。
ダイアログボックスごとに、 [TAB] は使えるようにするのか、それとも [TAB] キーの機能はいらないのか、細かいやりくりも簡単にできるので、以上は有効な方法だと思います。
※ [TAB]キーの機能というのは、[TAB] キーを押すごとに、ダイアログボックス内のフォーカスが次のコントロールアイテムに移る機能。
※ モーダルダイアログボックスは、そのダイアログボックスが表示されている間、ほかのウィンドウを操作できない、フォーカスの移動範囲が「限定」されたタイプのダイアログボックス。対するモードレスダイアログボックスは、ウィンドウ間で自由にフォーカスを行き来できます。
※ IsDialogMessage() 関数は、モードレスダイアログ以外でも使えます。
スケルトンコードというからには、最小限にとどめなくてはならないので、この仕組みは加えませんでしたが、以上で述べたことはいい設計だと思います。メッセージの前処理を必要とする場合は時折あるからです。
Windows API にもこの PreTranslate の仕組みがあっていいくらいには思いますが、存在しないのは、以上に述べたように簡単に設計できてしまうからだと思います。
作り込む予定のアプリケーションなら実際、これくらいしておくべきです。
[トップ] [戻る]