ユーザー インターフェイス特権の分離 (UIPI)と、ウインドウメッセージ(SendMessage,PostMessage,ChangeWindowMessageFilter)

Windows Vista ならびに Windows 2008 からは、セキュリティが強化されたためウインドウメッセージを使用した処理がOSによってフィルタリングされるケースがあります。


この問題は、少しこみいっていてSendMessageやPostMessageはフィルタリングされた場合でも正常応答を返すためフィルタリングされていることにウインドウメッセージ送信側のアプリは送信したことに気づきません。受信側も、そもそもメッセージが送られてこないので、この不具合を検出することは出来ないといった事にあります。


話をややこしくしているのが、これらの処理がXPまでは正常に動作していて、Vistaからエラー無しに通っているように見えて実は失敗しているという状況にあります。


このあたりの事情は以下のMSDNのトピックを参照。


Windows Vista® および Windows Server® 2008 アプリケーション互換性解説書 (ユーザー インターフェイス特権の分離 (UIPI))
http://msdn.microsoft.com/ja-jp/library/aa480152.aspx


対処としては、受信側のアプリでChangeWindowMessageFilterというウインドウメッセージのフィルタリングAPIを使用してウインドウメッセージを受信できるようフィルタリングのルールを変更するようOSに通知する必要があります。但し、ChangeWindowMessageFilterはVista以降のAPIであるためXP環境で実行するとエラーになってしまいます。(DLLにエントリポイントが存在しないので)ですが、XPの場合はフィルタリング自体が存在しないので、APIを呼ぶ必要自体が無いことになります。


つまり、Vista以降であれば呼び出し、XP以前であれば呼ばないというような分岐が必要になりますが、これは単純にChangeWindowMessageFilterがOSに実装されていれば呼ぶし無ければ呼ばないという処理で対応出来ると整理できます。


そのため、ChangeWindowMessageFilterはLoadLibraryを使用した実行時ロードを使って呼び出しを行う事になります。


実行時ロードについては以下を参照
Win32 API を実行時に動的ロードする。(LoadLibrary,GetProcAddress,FreeLibrary)
http://d.hatena.ne.jp/tekk/20091018/1255880682


ChangeWindowMessageFilterの使用例



Private Delegate Function ChangeWindowMessageFilter(ByVal message As UInteger, ByVal dwflag As Int32) As Boolean
Private Const MSGFLT_ADD As Integer = 1
Private Const MSGFLT_REMOVE As Integer = 2


Dim loader As New DynamicLibraryLoader
Dim result As Boolean = loader.Load("user32.dll")
If result = True Then
Try
Dim procPtr As [Delegate] = loader.GetDelegate("ChangeWindowMessageFilter", GetType(ChangeWindowMessageFilter))
If Not procPtr Is Nothing Then

Dim funcPtr As ChangeWindowMessageFilter = CType(procPtr, ChangeWindowMessageFilter)

funcPtr(WM_COPYDATA, MSGFLT_ADD)
funcPtr(WM_IPC_MESSAGE, MSGFLT_ADD)

End If

Finally
loader.Free()
End Try
End If

MSGFLT_ADDを指定すると、該当ウインドウメッセージをフィルタリングから外す(受信できる)事が出来るようになり、MSGFLT_REMOVEは、その逆のことが出来るようになります。