Windows OSでは、ユーザーからの入力や操作はすべてメッセージ(以下、Windowsメッセージ)として処理される。例えばマウス・カーソルが移動された場合には「WM_MOUSEMOVE」というIDのWindowsメッセージがウィンドウに送られるため、プログラムではユーザーによるマウス・カーソルの移動を知ることができる。
しかし.NETのWindowsフォームでは、たいていの場合、直接Windowsメッセージをプログラムで処理する必要はなく、フォームのメンバであるWndProcメソッドが、受け取ったWindowsメッセージを.NETのイベントに置き換えてくれる*。WM_MOUSEMOVEメッセージがフォームに送られてきたら、フォームでMouseMoveイベントが発生するといった具合だ。実際この場合には、MouseMoveイベントのイベント・ハンドラを記述すれば、マウス移動時の処理を記述できる。
*実際には、フォーム(Formクラス)の基本クラスであるControlクラスのWndProcメソッドでも、多くのWindowsメッセージが処理されている。
だが、すべてのWindowsメッセージがWndProcメソッドでイベントに置き換えられるわけではない。このため少し特殊な処理をフォームに実装したい場合などでは、プログラムで直接Windowsメッセージを取得し処理しなければならないこともある。本稿ではそのようなケースでの処理について解説する。
WndProcメソッドのオーバーライド
フォームに送られてくるWindowsメッセージを直接プログラムで処理したい場合には、Formクラス(System.Windows.Forms名前空間)のWndProcメソッドを自分のフォームでオーバーライドすればよい。
Visual Studioを利用している場合、基本クラスのメソッドのオーバーライドは、コード画面でフォームのコードを開き、C#の場合には「override」、VBの場合には「Overrides」と入力すればオーバーライド可能なメソッドが一覧され、以下のようなメソッドを簡単にコード内に挿入できる。
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
}
Protected Overrides Sub WndProc(ByRef m As Message)
MyBase.WndProc(m)
End Sub
Visual Studioを利用している場合、C#の場合には「override」、VBの場合には「Overrides」と入力すれば、このようなメソッドのひな型が自動挿入される。
このメソッドはフォームにWindowsメッセージが送られるたびに呼び出される。また、メソッドのパラメータであるMessage構造体(System.Windows.Forms名前空間)のオブジェクトが、フォームに送られてきたWindowsメッセージを表している*。
*「m.Msg」の値を画面に表示するようにすれば、マウス・カーソルの移動時には512番(WM_MOUSEMOVEメッセージのIDの値。16進数では「200」)のメッセージが送られているのが分かるはずだ。
このようにしてフォームでWndProcメソッドをオーバーライドすれば、実際にウィンドウに送られてきているすべてのWindowsメッセージをチェックできるため、.NETのイベントに対応していないWindowsメッセージの処理が可能だ。以下にその一例を示す。
[×]ボタンで閉じないウィンドウ
ここでは、WM_NCHITTESTメッセージを処理する例を示す。このメッセージはすべてのマウス入力メッセージに先立って送られてくるメッセージで、このメッセージが送られてくると、フォームのWndProcメソッドはウィンドウ内のどの部分にマウス・カーソルがあるのかを返すことになっている(返す結果はMessageオブジェクトのResultプロパティにセットされる)。
例えば、マウス・カーソルがウィンドウ右上隅にある[×]ボタン上にある場合には、WndProcメソッドでの処理の結果、Resultプロパティの値は「20」となる。そこで、WndProcメソッドを以下のような内容でオーバーライドして、Resultプロパティの値が「20」のときに限り、その値をマウス・カーソルがどこも指していない状態である「0」に置き換えれば、[×]ボタンをクリックしても閉じないフォームが出来上がる。
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
if (m.Msg == 0x84) { // 0x84 : WM_NCHITTEST
if (m.Result == (IntPtr)20) { // 20 : HTCLOSE
m.Result = (IntPtr)0; // 0 : HTNOWHERE
}
}
}
Protected Overrides Sub WndProc(ByRef m As Message)
MyBase.WndProc(m)
If m.Msg = &H84 Then ' &H84 : WM_NCHITTEST
If m.Result = CType(20, IntPtr) Then ' 20 : HTCLOSE
m.Result = CType(0, IntPtr) ' 0 : HTNOWHERE
End If
End If
End Sub
このコードを試すには、Visual StudioでWindowsアプリケーションのプロジェクトを新規作成し、Form1.csを開いてForm1クラスのメンバとしてこのメソッドを追加すればよい。
なお、このコード中で使用している3つの定数(16進数の「84」や「20」「0」)は、プラットフォームSDKに含まれるC/C++言語向けのヘッダ・ファイルであるWinUser.hで定義されているものだ*。
*WinUser.hファイルはVisual Studio 2005 Professional Editionなどには含まれている。それ以外のエディションを使用している場合は、「Microsoft Download Center: Windows Server 2003 R2 Platform SDK Web Install」からプラットフォームSDK(PSDK-x86.exe)の[Microsoft Windows Core SDK]−[Build Environment]−[Build Environment (x86 32-bit)]をインストールすれば、インストールしたプラットフォームSDKのIncludeフォルダ内に配置されるはずだ。また、MSDNでは公開されていないが、Googleなどで検索すれば、実際にそのファイルの内容を公開しているサイトが見つかる。
WinUser.hでは、これらの定数には上記コードのコメントにあるように、「WM_NCHITTEST」「HTCLOSE」「HTNOWHERE」という定数名が付けられている。Windowsメッセージを直接処理するコードを記述するには、プラットフォームSDKのヘルプやWinUser.hの内容をよく調べる必要がある。
カテゴリ:Windowsフォーム 処理対象:ウィンドウ
使用ライブラリ:Formクラス(System.Windows.Forms名前空間)
使用ライブラリ:Message構造体(System.Windows.Forms名前空間)
Copyright© Digital Advantage Corp. All Rights Reserved.