System.Windows.Forms.NativeWindowによる、メッセージ専用ウインドウの作成

サンプルコードNativeWindowMessageOnly.zip 直

NativeWindowクラスは、ウインドウ処理の基本となる処理をまとめたクラスで、ウインドウメッセージ(SendMessage,PostMessage)とウインドウプロシージャ(WndProc)の処理をカプセル化するための処理が集まっています。今回はこの機能を利用してメッセージ専用ウインドウを作成します。

メッセージ専用ウインドウとは、その名前の通りメッセージのみを扱うウインドウです。ウインドウサイズは0で画面に表示されません。ウインドウというとFormやButtonなどの画面の構成要素を想像しますが、表示しないウインドウも作成する事ができます。使用用途は、プロセス間通信に使用するWM_COPYDATAやウインドウメッセージのリフティングになります。また、アドインなど画面を持たないプログラム上でウインドウメッセージのリフティングをするさいの対象となるウインドウのために利用することもできます。

この場合にシンプルなウインドウ処理を実装しているNativeWindowクラスが基本クラスとなります。

メッセージ専用ウインドウ。ウインドウメッセージのリフティング処理で、現在のスレッドがIDLE(暇)になった場合に処理を実行します。コンストラクタでサイズ0でウインドウハンドルを作成し、Finalizeメソッドをオーバーライドしてウインドウハンドルを破棄しています。


Imports System.Runtime.InteropServices

Public Class MessageOnlyWindow
Inherits NativeWindow

_
Private Shared Function PostMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Boolean
End Function

Public Const WM_USER As UInteger = &H400
Public Const WM_IDLE As UInteger = WM_USER + 1

Private Const WS_BORDER As Int32 = &H800000

Public Sub New()

MyBase.New()

Dim cp As New CreateParams

cp.X = 0
cp.Y = 0
cp.Height = 0
cp.Width = 0

cp.Style = WS_BORDER

Me.CreateHandle(cp)

End Sub

Protected Overrides Sub Finalize()

Me.DestroyHandle()

End Sub

Public Sub PostIdleMessage()
PostMessage(Me.Handle, WM_IDLE, IntPtr.Zero, IntPtr.Zero)
End Sub

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

If m.Msg = WM_IDLE Then

Debug.Print("IDLEメッセージを受信しました。")

Exit Sub
End If

MyBase.WndProc(m)

End Sub

End Class

検証用のコードです。処理の先頭でIDLEメッセージをメッセージ専用ウインドウに対してPostMessageしていますが、処理が行われるのはSleep処理のあとの5秒後であることを確認します。(5秒間は現在のスレッドはSleepするため)


Public Class Form1

Private _messageOnlyWindow As New MessageOnlyWindow

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

_messageOnlyWindow.PostIdleMessage()

System.Threading.Thread.Sleep(5 * 1000)

End Sub

End Class

5秒後にイミディエイトウインドウに以下のように出力されます。

IDLEメッセージを受信しました。

今回はウインドウメッセージがメッセージキューの末尾に入ること、現在のスレッドが終了するまでメッセージキューが処理されないことを確認するために処理の先頭でIDLEメッセージを送信していますが、ウインドウメッセージのリフティング処理では通常は処理の末尾でPostMessageを行います。ある処理で画面操作を行った場合に内部的に様々なメッセージが画面の部品に対してPostMessageされている可能性があるためです。先頭でPostMessageしてしまうと、処理が終わるまでに様々なメッセージがメッセージキューに入る場合があります。

gsf_zero1さんもNativeWindowについて記事を書かれてたので紹介します。1年も前にすでに記載されてました!
[C#] .NET クラスライブラリ探訪-016 (System.Windows.Forms.NativeWindow) (WndProcオーバーライド, メッセージ)
http://d.hatena.ne.jp/gsf_zero1/20080903/p1