連載
» 2006年06月30日 05時00分 公開

.NET TIPS:WindowsフォームでWindowsメッセージを直接処理するには?

[遠藤孝信,デジタルアドバンテージ]
「.NET TIPS」のインデックス

連載目次

 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

フォームのWndProcメソッドをオーバーライドするメソッドのひな型(上:C#、下:VB)
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

ウィンドウを[×]ボタンで閉じなくするWndProcメソッド(上:C#、下:VB)
このコードを試すには、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名前空間)


「.NET TIPS」のインデックス

.NET TIPS

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。