経緯

最近、周りで VB.NET を使っている人が複数人出てきた恐らく2020年にWindows7 サポート終了となるため VB6 => VB.NET へのコンバージョンの案件が増えていると思われる そのため、以前行った際に苦労したふりがな機能について備忘録として残しておくことにする

実際のコード

ふりがな取得機能の実装クラス

  • 以下のふりがなに対応(半角および全角英数はそのまま取得)
    • ひらがな
    • 全角カナ
    • 半角カナ
Option Strict Off
Option Explicit On

Imports System.Runtime.InteropServices
Delegate Function fncDelegate(ByVal hWnd As Integer, ByVal uMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

''' <summary>
''' 変換イベントクラス
''' </summary>
Public Class ConvertedEventArgs
  Inherits EventArgs

  ''' <summary>
  ''' 変換イベントクラスのコンストラクタ
  ''' </summary>
  ''' <param name="f">ふりがな文字列</param>
  ''' <param name="r">変換後文字列</param>
  Public Sub New(ByVal f As String, ByVal r As String)
    Furigana = f
    Result = r
  End Sub

  Public ReadOnly Property Furigana() As String
  Public ReadOnly Property Result() As String
End Class

''' <summary>
''' 自動ふりがな取得クラス
''' </summary>
Public Class Imecomp
  Private m_TxtSrcControl As Control
  Private m_TxtDestControl As Control
  Private WithEvents M_MsgListner As MsgListner
  Private ReadOnly m_StrConvMode As Integer

  ''' <summary>
  ''' ふりがな取得完了通知イベント
  '''
  ''' ふりがな取得が完了したことを通知する
  ''' </summary>
  ''' <param name="txtSrc">ふりがな取得対象テキストボックス</param>
  ''' <param name="txtDest">ふりがな出力対象テキストボックス</param>
  ''' <param name="e">変換イベントクラス</param>
  Public Event Converted(ByVal txtSrc As System.Object, ByVal txtDest As System.Object, ByVal e As ConvertedEventArgs)

  ''' <summary>
  ''' メッセージリスナークラス
  ''' </summary>
  Private Class MsgListner
    Inherits NativeWindow

    'Windows Message
    Public Const WM_IME_COMPOSITION As Integer = &H10F
    Public Const WM_IME_ENDCOMPOSITION As Integer = &H10E
    Public Const WM_CHAR As Integer = &H102
    Public Const WM_KEYDOWN As Integer = &H100
    Public Const WM_KEYUP As Integer = &H101

    'IME APIs
    Declare Function ImmGetCompositionString Lib "imm32.dll" Alias "ImmGetCompositionStringA" (ByVal hIMC As Integer, ByVal dw As Integer, ByRef lpv As Byte, ByVal dw2 As Integer) As Integer
    Private Const GCS_COMPREADSTR As Integer = &H1
    Private Const GCS_COMPSTR As Integer = &H8
    Private Const GCS_RESULTREADSTR As Integer = &H200
    Private Const GCS_RESULTSTR As Integer = &H800
    Declare Function ImmGetOpenStatus Lib "imm32.dll" (ByVal hIMC As Integer) As Integer
    Declare Function ImmSetOpenStatus Lib "imm32.dll" (ByVal hIMC As Integer, ByVal fOpen As Boolean) As Integer
    Declare Function ImmGetContext Lib "imm32.dll" (ByVal hWnd As Integer) As Integer
    Declare Function ImmReleaseContext Lib "imm32.dll" (ByVal hWnd As Integer, ByVal hIMC As Integer) As Integer

    'Subclass APIs
    Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Integer, ByVal nIndex As Integer, <MarshalAs(UnmanagedType.FunctionPtr)> ByVal dwNewLong As fncDelegate) As Integer
    Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Integer, ByVal nIndex As Integer, ByVal dwNewLong As Integer) As Integer
    Private Const GWL_WNDPROC As Integer = (-4)
    Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Integer, ByVal hWnd As Integer, ByVal msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

    Private m_Enabled As Boolean = True
    Private convMode As Integer = 0
    Private pramStrConvMode As Integer = 0

    Public Event Converted(ByVal imeCompString As String, ByVal resultConvString As String)

    Public Property Enabled() As Boolean
      Get
        Return m_Enabled
      End Get

      Set(ByVal Value As Boolean)
        m_Enabled = Value
      End Set
    End Property

    ''' <summary>
    ''' ふりがな取得対象コントロールのウィンドウプロシージャ
    ''' </summary>
    ''' <param name="m">Windows メッセージ</param>
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
      Dim hIMC As Integer
      Dim intLength As Integer

      If m_Enabled Then
        Select Case m.Msg
          Case WM_IME_COMPOSITION
            Dim bytBuff() As Byte
            Dim strResult As String = ""
            Dim strFurigana As String = ""

            hIMC = ImmGetContext(Me.Handle.ToInt32)

            '変換後文字列の取得
            intLength = ImmGetCompositionString(hIMC, GCS_RESULTSTR, 0, 0)
            If intLength > 0 Then
              ReDim bytBuff(intLength - 1)
              ImmGetCompositionString(hIMC, GCS_RESULTSTR, bytBuff(0), intLength)
              strResult = System.Text.Encoding.Default.GetString(bytBuff)
            End If

            'ふりがな文字列
            intLength = ImmGetCompositionString(hIMC, GCS_RESULTREADSTR, 0, 0)
            If intLength > 0 Then
              ReDim bytBuff(intLength - 1)
              ImmGetCompositionString(hIMC, GCS_RESULTREADSTR, bytBuff(0), intLength)
              strFurigana = System.Text.Encoding.Default.GetString(bytBuff)
              convMode = IIf(pramStrConvMode = 0, vbNarrow, pramStrConvMode)

              '追加する文字種の判断
              If ChrIsNarrow(StrConv(strResult, VbStrConv.Narrow)) Then
                strFurigana = strResult
              End If

              'テキストを追加
              Select Case convMode
                Case VbStrConv.Narrow
                  strFurigana = StrConv(strFurigana, VbStrConv.Narrow)
                Case VbStrConv.Katakana
                  strFurigana = StrConv(strFurigana, VbStrConv.Wide)
                Case Else
                  strFurigana = StrConv(StrConv(strFurigana, VbStrConv.Wide), convMode)
              End Select

              'イベント起動
              RaiseEvent Converted(strFurigana, strResult)
            End If

            ImmReleaseContext(Me.Handle.ToInt32, hIMC)
          Case WM_CHAR  '半角英数字
            hIMC = ImmGetContext(Me.Handle.ToInt32)
            If ImmGetOpenStatus(hIMC) = 0 Then
              If m.WParam.ToInt32 >= 32 Then
                'イベント起動
                RaiseEvent Converted(Chr(m.WParam.ToInt32), Chr(m.WParam.ToInt32))
              End If
            End If

            ImmReleaseContext(Me.Handle.ToInt32, hIMC)
        End Select
      End If

      MyBase.WndProc(m)
    End Sub

    ''' <summary>
    ''' メッセージリスナークラスのコンストラクタ
    ''' </summary>
    ''' <param name="target">入力文字</param>
    ''' <param name="strConvMode">文字変換モード</param>
    Public Sub New(ByVal target As IntPtr, ByRef strConvMode As Integer)
      pramStrConvMode = strConvMode
      AssignHandle(target)
    End Sub

    Protected Overrides Sub Finalize()
      ReleaseHandle()
      MyBase.Finalize()
    End Sub

    ''' <summary>
    ''' 半角文字かどうかを判定して返す
    ''' </summary>
    ''' <param name="target">ふりがな取得対象テキストボックス</param>
    ''' <returns>true:半角文字/false:半角文字以外</returns>
    Public Function ChrIsNarrow(ByRef target As String) As Boolean

      Dim nPt As Integer
      Dim NowPt As Integer
      Dim NowCode As Integer
      Dim fNarrow As Boolean

      fNarrow = True
      nPt = Len(target)
      For NowPt = 1 To nPt
        NowCode = Asc(Mid(target, NowPt, 1))
        If NowCode < 0 Or NowCode > 255 Then
          fNarrow = False
          Exit For
        End If
      Next NowPt

      ChrIsNarrow = fNarrow
    End Function
  End Class

  ''' <summary>
  ''' 自動ふりがな取得クラスのコンストラクタ
  ''' </summary>
  ''' <param name="txtSrc">ふりがな取得対象テキストボックス</param>
  ''' <param name="txtDest">ふりがな出力対象テキストボックス</param>
  ''' <param name="strConvMode">ふりがなの種類</param>
  Public Sub New(ByVal txtSrc As Control, txtDest As Control, strConvMode As Integer)
    M_MsgListner = New MsgListner(txtSrc.Handle, strConvMode)
    m_TxtSrcControl = txtSrc
    m_TxtDestControl = txtDest
  End Sub

  Public ReadOnly Property TxtSrcControl() As Control
    Get
      Return m_TxtSrcControl
    End Get
  End Property

  Public ReadOnly Property TxtDestControl() As Control
    Get
      Return m_TxtDestControl
    End Get
  End Property

  Public Property Enabled() As Boolean
    Get
      Return M_MsgListner.Enabled
    End Get

    Set(ByVal Value As Boolean)
      M_MsgListner.Enabled = Value
    End Set
  End Property

  Private Sub M_MsgListner_Converted(ByVal imeCompString As String, ByVal resultConvString As String) Handles M_MsgListner.Converted
    RaiseEvent Converted(Me.TxtSrcControl, Me.TxtDestControl, New ConvertedEventArgs(imeCompString, resultConvString))
  End Sub
End Class

ふりがな取得機能テスト用フォーム

初期処理時にふりがな種別を指定する

Imports Microsoft.VisualBasic

Public Class Form1
  Private WithEvents ImeComp As Imecomp 'ふりがな取得イベント

  Private Sub Frm1_Load(ByVal eventSender As System.Object, ByVal eventArgs As System.EventArgs) Handles MyBase.Load
    ImeComp = New Imecomp(Me.TextBox1, Me.TextBox2, VbStrConv.Hiragana) 'ふりがなの種類(ひらがな)
    'imeComp = New Imecomp(Me.TextBox1, Me.TextBox3, VbStrConv.Katakana) 'ふりがなの種類(全角カナ)
    'ImeComp = New Imecomp(Me.TextBox1, Me.TextBox4, VbStrConv.Narrow) 'ふりがなの種類(半角カナ)
  End Sub

  Private Sub ImeComp_Converted(ByVal txtSrc As Object, ByVal txtDest As Object, ByVal e As ConvertedEventArgs) Handles ImeComp.Converted
    txtDest.Text &= e.Furigana
    If Len(txtDest.Text) > txtDest.MaxLength Then
      txtDest.Text = Strings.Left(txtDest.Text, txtDest.MaxLength)
    End If
  End Sub

  Private Sub TextBox1_TextChanged(sender As Object, e As EventArgs) Handles TextBox1.TextChanged
    If TextBox1.Text = "" Then
      TextBox2.Text = ""
      TextBox3.Text = ""
      TextBox4.Text = ""
    End If
  End Sub
End Class

実行結果イメージ

ふりがな自動取得機能 実行結果
ふりがな自動取得機能 実行結果