none
PreFilterMessage で WM_IME_CHAR がフィルタリングされない RRS feed

  • 質問

  • PreFilterMessage で WM_IME_CHAR を処理しようとしたところ、
    WM_CHAR は届くものの、WM_IME_CHAR が届きません。

    これは、仕様によるものでしょうか。
    それとも、使い方や考え方に誤りがあるのでしょうか。

    また、アプリケーション内の全コントロールの WM_IME_CHAR を受け取り、
    排除できる代替方法はありますでしょうか。
    (SetWindowsHookEx の WH_CALLWNDPROC では受け取れるが排除はできず…。)


    確認環境は、Windows 7 SP1 Pro 64bit、Visual Studio 2013 で、
    対象フレームワークは 4.7.1 です。

    再現方法は以下の通りです。


    【EXE の作成】---------------------------------------------------

      (1) Visual Studio で、Visual Basic の
          Windows アプリケーション プロジェクトを作成。

      (2) フォームのソースに下記を記入して実行。
    Public Class Form1
    
      Private Const WM_KEYDOWN As Integer = &H100
      Private Const WM_CHAR As Integer = &H102
      Private Const WM_IME_CHAR As Integer = &H286
    
      Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    
        Dim txt As New myTextBox
    
        txt.Location = New System.Drawing.Point(30, 30)
        txt.ImeMode = ImeMode.On
    
        Me.Controls.Add(txt)
    
        Application.AddMessageFilter(New myMsgFilter())
    
      End Sub
    
      Private Class myTextBox
        Inherits TextBox
    
        Protected Overrides Sub WndProc(ByRef m As Message)
    
          Select Case m.Msg
            Case WM_KEYDOWN
              Debug.WriteLine("WndProc - WM_KEYDOWN : " & CType(m.WParam.ToInt32, Keys).ToString)
            Case WM_CHAR
              Debug.WriteLine("WndProc - WM_CHAR : " & ChrW(m.WParam.ToInt32))
            Case WM_IME_CHAR
              Debug.WriteLine("WndProc - WM_IME_CHAR : " & ChrW(m.WParam.ToInt32))
          End Select
    
          MyBase.WndProc(m)
    
        End Sub
    
      End Class
    
      Private Class myMsgFilter
        Implements IMessageFilter
    
        Public Function PreFilterMessage(ByRef m As Message) As Boolean Implements IMessageFilter.PreFilterMessage
    
          Select Case m.Msg
            Case WM_KEYDOWN
              Debug.WriteLine("PreFilt - WM_KEYDOWN : " & CType(m.WParam.ToInt32, Keys).ToString)
            Case WM_CHAR
              Debug.WriteLine("PreFilt - WM_CHAR : " & ChrW(m.WParam.ToInt32))
            Case WM_IME_CHAR
              Debug.WriteLine("PreFilt - WM_IME_CHAR : " & ChrW(m.WParam.ToInt32))
          End Select
    
          Return False
    
        End Function
    
      End Class
    
    End Class

    【再現手順】----------------------------------------------------

      [A]キー、[Enter]キーの順に押下。

      ⇒出力ウィンドウに次の通り表示される。
        (PreFilterMessage に WM_IME_CHAR が届いていない。)

          PreFilt - WM_KEYDOWN : ProcessKey
          WndProc - WM_KEYDOWN : ProcessKey
          PreFilt - WM_KEYDOWN : ProcessKey
          WndProc - WM_KEYDOWN : ProcessKey
          WndProc - WM_IME_CHAR : あ
          PreFilt - WM_CHAR : あ
          WndProc - WM_CHAR : あ

    【ここまで】----------------------------------------------------

    2018年1月22日 8:09

すべての返信

  • Windowのメッセージ処理というのは、GUIスレッドの中でOS経由で届いているウィンドウ宛のメッセージをループで取り出して、そのメッセージがPreFilterMessegeでフィルタされなかったら、そのGUIスレッドに属する各ウィンドウのWndProcに分配してます。
    ですが、WndProcは関数でしかないので、このメッセージ処理以外でも呼び出すことができます。
    そうやってよびだされた場合にはPreFilterMessageは通らないのでフィルタリングはできないという事になります。
    WM_IME_CHARはそのようなメッセージなのでしょう。

    Public Class Form1
    
        Private Const WM_KEYDOWN As Integer = &H100
        Private Const WM_CHAR As Integer = &H102
        Private Const WM_IME_CHAR As Integer = &H286
    
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    
            Dim txt As New myTextBox
    
            txt.Location = New System.Drawing.Point(30, 30)
            txt.ImeMode = ImeMode.On
    
            Me.Controls.Add(txt)
    
            Application.AddMessageFilter(New myMsgFilter())
        End Sub
    
    
        Protected Overrides Sub WndProc(ByRef m As Message)
            Tab() : System.Diagnostics.Debug.WriteLine(">WND:" & vbTab & WMToString(m.Msg))
    
            MyBase.WndProc(m)
    
            Tab() : System.Diagnostics.Debug.WriteLine("<WND:" & vbTab & WMToString(m.Msg))
        End Sub
    
        Private Class myTextBox
            Inherits TextBox
    
            Protected Overrides Sub WndProc(ByRef m As Message)
                Tab() : System.Diagnostics.Debug.WriteLine(">TXB:" & vbTab & WMToString(m.Msg))
                stack += 1
                MyBase.WndProc(m)
                stack -= 1
                Tab() : System.Diagnostics.Debug.WriteLine("<TXB:" & vbTab & WMToString(m.Msg))
                If (m.Msg = WM_IME_CHAR) Then
                    ' Debugger.Break()
                End If
            End Sub
    
        End Class
    
        Private Class myMsgFilter
            Implements IMessageFilter
    
            Public Function PreFilterMessage(ByRef m As Message) As Boolean Implements IMessageFilter.PreFilterMessage
                Tab() : System.Diagnostics.Debug.WriteLine("*PRE:" & vbTab & WMToString(m.Msg))
                Return False
            End Function
    
        End Class
    
        Private Shared stack As Integer = 0
        Private Shared Sub Tab()
            System.Diagnostics.Debug.Write(New String(" "c, stack * 2))
        End Sub
        Private Shared Function WMToString(ByVal wm As Integer) As String
            If ([Enum].IsDefined(GetType(WM), wm)) Then
                Return CType(wm, WM).ToString()
            Else
                Return wm.ToString("X04")
            End If
    
        End Function
        Enum WM
            WM_ENABLE = &HA
            WM_SETREDRAW = &HB
            WM_SETTEXT = &HC
            WM_GETTEXT = &HD
            WM_GETTEXTLENGTH = &HE
            WM_PAINT = &HF
    
            WM_KEYDOWN = &H100
            WM_KEYUP = &H101
            WM_CHAR = &H102
    
            WM_IME_STARTCOMPOSITION = &H10D
            WM_IME_ENDCOMPOSITION = &H10E
            WM_IME_COMPOSITION = &H10F
    
            WM_IME_SETCONTEXT = &H281
            WM_IME_NOTIFY = &H282
            WM_IME_CONTROL = &H283
            WM_IME_COMPOSITIONFULL = &H284
            WM_IME_SELECT = &H285
            WM_IME_CHAR = &H286
            WM_IME_REQUEST = &H288
            WM_IME_KEYDOWN = &H290
            WM_IME_KEYUP = &H291
        End Enum
    End Class


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2018年1月22日 16:51
  • ご返信ありがとうございます。

    いろいろと試してみたところ、
    PostMessage で送信したメッセージは PreFilterMessage に届くものの、
    SendMessage で送信したメッセージは届かないようですね。

    また、SetWindowsHookEx でフックしたところ、
    WH_GETMESSAGE では WM_CHAR は届くが WM_IME_CHAR は届かず、
    WH_CALLWNDPROC では WM_CHAR は届かないが WM_IME_CHAR は届くようです。


    頂いたご回答と、試した結果から考えるに、

    ・PreFilterMessage で受け取れるメッセージは、
      PostMessage 等でポストされた(キューに入った)メッセージのみ。

    ・SendMessage や WndProc の直接呼出し等、
      キューにポストせずに即時処理されるメッセージは、
      PreFilterMessage では受け取れない。

    ・WM_CHAR はキューにポストされているが、
      WM_IME_CHAR はポストせずに処理されているため、
      PreFilterMessage では処理できない。

    というところでしょうか。


    すると、代替方法で処理するにも、仕組み的に無理ということですかね?
    せめて、wParam の書き換えだけでもできるといいのですが…。
    2018年1月23日 1:34