2014年12月26日金曜日

IMEからカナを取得

IME入力時に読みを別コントロールに設定できる仕様を追加して欲しいとの事。


結局、流れた案件だけど、ある程度の調査結果でも書いておきます。


(たまには技術屋らしい書き込みもしないとねσ(^_^汗


調べると、IME用のAPIを使うとの事。
で、WndProcをOverridesして操作が常道らしい。


まず、ModuleにAPI等を定義
------------------------------------------------------------
Imports System.Runtime.InteropServices


Public Module TestFuncs
#Region "API定数"
    ''' <summary>WindowMessage:WM_CHAR</summary>
    Public Const WM_CHAR As Integer = &H102
    ''' <summary>WindowMessage:WM_IME_COMPOSITION</summary>
    Public Const WM_IME_COMPOSITION As Integer = &H10F
    ''' <summary>ImmGetCompositionString用定数:GCS_RESULTREADSTR</summary>
    Public Const GCS_RESULTREADSTR As Integer = &H200
#End Region
#Region "API定義"
    ''' <summary>IMEハンドル取得</summary>
    <DllImport("Imm32.dll")> _
    Public Function ImmGetContext(ByVal hWnd As Integer) As Integer
    End Function
    ''' <summary>IMEハンドル解放</summary>
    <DllImport("Imm32.dll")> _
    Public Function ImmReleaseContext(ByVal hWnd As Integer, ByVal hIMC As Integer) As Integer
    End Function
    ''' <summary>フリガナ取得</summary>
    <DllImport("Imm32.dll")> _
    Public Function ImmGetCompositionString(ByVal hIMC As Integer, ByVal dwIndex As Integer, ByVal lpBuf As StringBuilder, ByVal dwBufLen As Integer) As Integer
    End Function
    ''' <summary>IME状態取得</summary>
    <DllImport("Imm32.dll")> _
    Public Function ImmGetOpenStatus(ByVal hIMC As Integer) As Integer
    End Function
#End Region
End Moudle
------------------------------------------------------------


あとはテキストボックス継承のコントロールを作成
------------------------------------------------------------
Imports System.ComponentModel


Public Class TestTextBox
    Inherits System.Windows.Forms.TextBox


    ''' <summary>読み設定先</summary>
    Private _YomiControl As Control = Nothing


    ''' <summary>読み設定先コントロール</summary>
    <Category("カスタム")> _
    <Description("読み設定先コントロール")>
    Public Property YomiControl() As Control
        Get
            Return _YomiControl
        End Get
        Set(ByVal value As Control)
            _YomiControl = value
        End Set
    End Property


#Region "WndProcフック"
    ''' <summary>ウィンドウプロシージャ</summary>
    Protected Overloads Overrides Sub WndProc(ByRef wm As System.Windows.Forms.Message)
        Try
            If Not _YomiControl Is Nothing Then
                Select Case wm.Msg
                    Case WM_IME_COMPOSITION
                        'IME確定
                        Dim InpStr As String = ""
                        If (CUInt(wm.LParam) And CUInt(GCS_RESULTREADSTR)) <> 0 Then
                            Dim hIM As Integer = ImmGetContext(Me.Handle.ToInt32())
                            Dim strLen = ImmGetCompositionString(hIM, GCS_RESULTREADSTR, Nothing, 0)
                            If strLen > 0 Then
                                Dim temp As New System.Text.StringBuilder(strLen)
                                ImmGetCompositionString(hIM, GCS_RESULTREADSTR, temp, strLen)
                                InpStr = temp.ToString()
                                If InpStr.Length > strLen Then
                                    InpStr = InpStr.Substring(0, strLen)
                                End If
                                Try
                                    If Not _YomiControl Is Nothing Then
                                        _YomiControl.Text = _YomiControl.Text & StrConv(InpStr, VbStrConv.Wide)
                                    End If
                                Catch ex As Exception
                                End Try
                            End If
                            ImmReleaseContext(Me.Handle.ToInt32(), hIM)
                        End If
                        Exit Select
                    Case WM_CHAR
                        '直接入力
                        Dim hIM As Integer = ImmGetContext(Me.Handle.ToInt32())
                        If ImmGetOpenStatus(hIM) = 0 Then
                            Dim InpChr As Char = Convert.ToChar(wm.WParam.ToInt32() And &HFF)
                            If wm.WParam.ToInt32() >= 32 Then
                                Dim InpStr As String = InpChr.ToString()
                                Try
                                    If Not _YomiControl Is Nothing Then
                                        _YomiControl.Text = _YomiControl.Text & InpStr
                                    End If
                                Catch ex As Exception
                                End Try
                            End If
                        End If
                        ImmReleaseContext(Me.Handle.ToInt32(), hIM)
                        Exit Select
                End Select
            End If
        Catch ex As Exception
        End Try
        MyBase.WndProc(wm)
    End Sub
#End Region
End Class
------------------------------------------------------------
とりあえずカナを取得して、指定の別コントロールに設定するソース。


2時間程度で調べて作ったものなのでバグ等があるやもしれませんがw