|
.NET TIPS
IMEで入力された日本語の読みを取得するには?[C#、VB]
デジタルアドバンテージ 岸本 真二郎
2009/05/14 |
|
|
アプリケーションでユーザーに氏名や住所などを入力してもらう際、漢字での入力とともに、その“読み”もほしい場合がある。このような場合、わざわざ読みを入力してもらわなくとも、IMEに入力された読みを漢字入力が確定した時点でIMEから取得できる。
例えば、ユーザーがIMEをONにしてテキストボックスに「にほんご」と入力し、変換候補として「日本語」が表示された後、確定キーを押した時点で、入力された「ニホンゴ」という文字列をIMEから取得できる(ただし、漢字入力が確定された直後にIMEをOFFにされると、読みの取得はできない)。
このような処理は、IMEを制御するWin32 APIであるImmGetCompositionString関数を呼び出すことにより実現できるが、これをC#やVBから呼び出すには、いくつかの手順が必要になる。
本稿では、漢字入力用と読み表示用の2つのテキストボックスを持つWindowsフォームを作成し、漢字入力が確定したら、その読みを表示するサンプル・アプリケーションを実装しながら、IMEから読みを取得する方法を紹介する。
|
サンプル・アプリケーションの実行画面 |
上側のテキストボックスに漢字を入力して確定すると、下側のテキストボックスに読みがセットされる。 |
なお、本稿で作成しているサンプル・アプリケーションのVisual Studio 2008用プロジェクトは以下のリンクよりダウンロードできる。
以降では、上記プロジェクトに含まれるソース・ファイルより主要な部分を抜き出して解説する。
Win32 APIの準備
まずは、IME関係のWin32 APIをC#/VBから呼び出せるよう準備しておく。
private const int WM_CHAR = 0x102;
private const int WM_IME_COMPOSITION = 0x10F;
private const int GCS_RESULTREADSTR = 0x0200;
// コンテキスト・ハンドルの取得
[DllImport("Imm32.dll")]
private static extern int ImmGetContext(int hWnd);
// コンテキスト・ハンドルの解放
[DllImport("Imm32.dll")]
private static extern int ImmReleaseContext(int hWnd, int hIMC);
// IMEより読みなどの文字列を取得する
[DllImport("Imm32.dll")]
private static extern int ImmGetCompositionString(int hIMC, int dwIndex, StringBuilder lpBuf, int dwBufLen);
// IMEの状態取得
[DllImport("Imm32.dll")]
private static extern int ImmGetOpenStatus(int hIMC);
|
Private Const WM_CHAR As Integer = &H102
Private Const WM_IME_COMPOSITION As Integer = &H10F
Private Const GCS_RESULTREADSTR As Integer = &H200
' コンテキスト・ハンドルの取得
<DllImport("Imm32.dll")> _
Private Shared Function ImmGetContext(ByVal hWnd As Integer) As Integer
End Function
' コンテキスト・ハンドルの解放
<DllImport("Imm32.dll")> _
Private Shared Function ImmReleaseContext(ByVal hWnd As Integer, ByVal hIMC As Integer) As Integer
End Function
' IMEから読みなどの文字列を取得する
<DllImport("Imm32.dll")> _
Private Shared Function ImmGetCompositionString(ByVal hIMC As Integer, ByVal dwIndex As Integer, ByVal lpBuf As StringBuilder, ByVal dwBufLen As Integer) As Integer
End Function
' IMEの状態取得
<DllImport("Imm32.dll")> _
Private Shared Function ImmGetOpenStatus(ByVal hIMC As Integer) As Integer
End Function
|
|
Win32APIを呼び出す準備(上:C#、下:VB) |
コントロールのサブクラス化と読みの取得
IMEから読みを取得するには、IME関連のWindowsメッセージを監視する必要がある。これにはNativeWindowクラス(System.Windows.Forms名前空間)を使って、日本語が入力されるコントロールをサブクラス化し、コントロールに送られるWindowsメッセージをチェックする。具体的には、IMEでの漢字変換が確定すると「WM_IME_COMPOSITION」というメッセージが送られてくるので、これを監視する。
ここではまず、MsgListenerというクラス名でNativeWindowクラスの派生クラスを作成し、コントロールをサブクラス化してWindowsメッセージの監視を行う。WndProcメソッドでWM_IME_COMPOSITIONメッセージ(0x10F)を受け取ったら、さらにメッセージのパラメータをチェックし、漢字の読みが確定したことを示す「GCS_RESULTREADSTR」が含まれる場合に、ImmGetCompositionString関数を使って読みの文字列を取得する。
なおIME関連の処理を行う場合、最初にIMEの「コンテキスト・ハンドル」を取得(ImmGetContext関数)し、処理が終わったらコンテキスト・ハンドルを解放(ImmReleaseContext関数)する必要がある。
private class MsgListener : NativeWindow
{
protected bool enabled = false; // メッセージの監視状態(On/Off)
public delegate void Converted(System.Object sender, ConvertedEventArgs e);
public event Converted OnConverted;
// コンストラクタ
public MsgListener(Control c) {
AssignHandle(c.Handle);
c.HandleDestroyed += new EventHandler(OnHandleDestroyed);
}
internal void OnHandleDestroyed(object sender, EventArgs e) {
ReleaseHandle();
}
public bool Enabled {
get { return enabled; }
set { enabled = value; }
}
// 読み取得対象コントロールのウィンドウ・プロシージャ
protected override void WndProc(ref System.Windows.Forms.Message m)
{
int hIMC;
int intLength;
if (enabled) {
switch (m.Msg) {
case WM_IME_COMPOSITION:
string strYomi = "";
if (((uint)m.LParam & (uint)GCS_RESULTREADSTR) != 0) {
hIMC = ImmGetContext(this.Handle.ToInt32());
// 読みの文字列
intLength = ImmGetCompositionString(
hIMC, GCS_RESULTREADSTR, null, 0);
if (intLength > 0) {
StringBuilder temp = new StringBuilder(intLength);
ImmGetCompositionString(
hIMC, GCS_RESULTREADSTR, temp, intLength);
strYomi = temp.ToString();
if (strYomi.Length > intLength) {
strYomi = strYomi.Substring(0, intLength);
}
// イベントを起動
ConvertedEventArgs ea = new ConvertedEventArgs(
strYomi, false, intLength);
OnConverted(this, ea);
}
ImmReleaseContext(this.Handle.ToInt32(), hIMC);
}
break;
case WM_CHAR: // '半角英数字
hIMC = ImmGetContext(this.Handle.ToInt32());
if (ImmGetOpenStatus(hIMC) == 0) {
char chr = Convert.ToChar(m.WParam.ToInt32() & 0xff);
if (m.WParam.ToInt32() >= 32) {
//イベント起動
string str = chr.ToString();
ConvertedEventArgs ea =
new ConvertedEventArgs(str, true, 1);
OnConverted(this , ea);
}
}
ImmReleaseContext(this.Handle.ToInt32(), hIMC);
break;
}
}
base.WndProc(ref m);
}
}
|
Private Class MsgListener
Inherits NativeWindow
Protected m_enabled As Boolean = False ' メッセージの監視状態(On/Off)
Public Delegate Sub Converted(ByVal sender As System.Object, ByVal e As ConvertedEventArgs)
Public Event OnConverted As Converted
' コンストラクタ
Public Sub New(ByVal c As Control)
AssignHandle(c.Handle)
AddHandler c.HandleDestroyed, AddressOf OnHandleDestroyed
End Sub
Friend Sub OnHandleDestroyed(ByVal sender As Object, _
ByVal e As EventArgs)
ReleaseHandle()
End Sub
Public Property Enabled() As Boolean
Get
Return m_enabled
End Get
Set(ByVal value As Boolean)
m_enabled = value
End Set
End Property
' 読み取得対象コントロールのウィンドウ・プロシージャ
Protected Overloads 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 strResult As String = ""
Dim strYomi As String = ""
If (CUInt(m.LParam) And CUInt(GCS_RESULTREADSTR)) <> 0 Then
hIMC = ImmGetContext(Me.Handle.ToInt32())
' 読みの文字列
intLength = ImmGetCompositionString( _
hIMC, GCS_RESULTREADSTR, Nothing, 0)
If intLength > 0 Then
Dim temp As New StringBuilder(intLength)
ImmGetCompositionString(_
hIMC, GCS_RESULTREADSTR, temp, intLength)
strYomi = temp.ToString()
If strYomi.Length > intLength Then
strYomi = strYomi.Substring(0, intLength)
End If
' イベント起動
Dim ea As New ConvertedEventArgs( _
strYomi, strResult, False, intLength)
RaiseEvent OnConverted(Me, ea)
End If
ImmReleaseContext(Me.Handle.ToInt32(), hIMC)
End If
Exit Select
Case WM_CHAR: '半角英数字
hIMC = ImmGetContext(Me.Handle.ToInt32())
If ImmGetOpenStatus(hIMC) = 0 Then
Dim chr As Char = _
Convert.ToChar(m.WParam.ToInt32() And &HFF)
If m.WParam.ToInt32() >= 32 Then
'イベント起動
Dim str As String = chr.ToString()
Dim ea As ConvertedEventArgs = _
new ConvertedEventArgs(str, true, 1)
RaiseEvent OnConverted(Me, ea)
End If
End If
ImmReleaseContext(Me.Handle.ToInt32(), hIMC)
Exit Select
End Select
End If
MyBase.WndProc(m)
End Sub
End Class
|
|
IMEのメッセージを処理するWinProcメソッド(上:C#、下:VB) |
またこのMsgListenerクラスでは、読みを取得できた際に、あらかじめ登録されたメソッドを呼び出せるように、OnConvertedイベントを実装している。
読みを取得するメイン・クラス
OnConvertedイベントに使用するイベント引数のクラスと、読みを取得するメインのクラスであるImeYomiConversionクラスは次のようになる。
ImeYomiConversionクラスでは、EventConvertedメソッドをOnConvertedイベントに登録している。これにより、読みの取得が完了したら、EventConvertedメソッドが呼び出され、IMEから取得した読みをテキストボックスにセットできる。
// イベント引数
public class ConvertedEventArgs : EventArgs
{
private string yomi; // 読み
private bool bSingle; // 半角文字か
private int yomiLength = 0; // 読みの文字数
public ConvertedEventArgs(string f, bool singleFlag, int friLen) {
yomi = f;
bSingle = singleFlag;
yomiLength = friLen;
}
public string YomiString {
get { return yomi; }
}
public int YomiLen {
get { return yomiLength; }
}
public bool IsSingleChar {
get { return bSingle; }
}
}
// 読みを取得するクラス
public class ImeYomiConversion
{
// targetCtrl:日本語入力を行うコントロール
// yomiCtrl:読みをセットするコントロール
public ImeYomiConversion(Control targetCtrl, Control yomiCtrl)
{
targetControl = targetCtrl;
this.targetYomiControl = yomiCtrl;
MsgListener = new MsgListener(targetCtrl); // サブクラス化
// 日本語入力が確定したらイベントで通知
MsgListener.OnConverted +=
new MsgListener.Converted(EventConverted);
}
public bool Enabled {
get { return MsgListener.Enabled;} // メッセージ監視のOn/Off
set { MsgListener.Enabled = value;} // メッセージ監視状態
}
// 変換処理が行われた
private void EventConverted(object sender, ConvertedEventArgs ea)
{
if (TargetControl.Text.Length == 0) {
// 漢字入力のコントロールが空なら読みも空に
targetYomiControl.Text = "";
}
string s = "";
if (ea.IsSingleChar) {
s = ea.FuriganaString;
} else {
s = Strings.StrConv(ea.FuriganaString, VbStrConv.Wide, 0x411);
}
// 読みをコントロールにセットする
targetYomiControl.Text += s;
}
}
|
' イベント引数
Public Class ConvertedEventArgs
Inherits EventArgs
Private yomi As String ' 読み
Private bSingle As Boolean ' 半角文字か
Private yomiLength As Integer = 0 ' 読みの文字数
Public Sub New(ByVal f As String, ByVal singleFlag As Boolean, ByVal friLen As Integer)
yomi = f
bSingle = singleFlag
yomiLength = friLen
End Sub
Public ReadOnly Property YomiString() As String
Get
Return yomi
End Get
End Property
Public ReadOnly Property YomiLen() As Integer
Get
Return yomiLength
End Get
End Property
Public ReadOnly Property IsSingleChar() As Boolean
Get
Return bSingle
End Get
End Property
End Class
' 読みを取得するクラス
Public Class ImeYomiConversion
' targetCtrl:日本語入力を行うコントロール
' yomiCtrl:読みをセットするコントロール
Public Sub New(ByVal targetCtrl As Control, ByVal yomiCtrl As Control)
targetControl = targetCtrl
Me.targetYomiControl = yomiCtrl
listner = New MsgListener(targetControl) ' サブクラス化
'日本語入力が確定したらイベントで通知
AddHandler listner.OnConverted, AddressOf EventConverted
End Sub
Public Property Enabled() As Boolean
' メッセージ監視のOn/Off
Get
Return listner.Enabled
End Get
' メッセージ監視状態
Set(ByVal value As Boolean)
listner.Enabled = value
End Set
End Property
' 変換処理が行われた
Private Sub EventConverted(ByVal sender As Object, ByVal ea As ConvertedEventArgs)
If targetControl.Text.Length = 0 Then
' 漢字入力のコントロールが空なら読みも空に
targetYomiControl.Text = ""
End If
Dim s As String = ""
If ea.IsSingleChar Then
s = ea.FuriganaString
Else
s = Strings.StrConv(ea.FuriganaString, VbStrConv.Wide, &H411)
End If
' 読みをコントロールにセットする
targetYomiControl.Text += s
End Sub
End Class
|
|
コントロールのサブクラス化とイベントの設定(上:C#、下:VB) |
IMEから取得した読みは半角カタカナの文字列なので、VBのライブラリを使って全角カタカナに変換している。 |
利用例
以上の処理を行うことで、IMEで入力した漢字に対応する読みを取得できるようになる。次に、2つのテキストボックスを配置したWindowsフォーム・アプリケーションで、上記のImeYomiConversionクラスを使って読みを取得する例を示す。
ここでは、ImeYomiConversionクラスのインスタンスを生成し、漢字の入力を行うテキストボックスのフォーカス移動に応じて、読みの取得処理をOn/Offしている。
public class Form1 : System.Windows.Forms.Form
{
private ImeYomiConversion yomiConv;
private void Form1_Load(object sender, System.EventArgs e) {
yomiConv = new ImeYomiConversion(this.TextBox1, this.TextBox2);
}
private void TextBox1_Enter(object sender, System.EventArgs e) {
yomiConv.Enabled = true;
}
private void TextBox1_Leave(object sender, System.EventArgs e) {
yomiConv.Enabled = false;
}
}
|
Public Class Form1
private yomiConv As ImeComposition.ImeYomiConversion
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
yomiConv = new ImeComposition.ImeYomiConversion(TextBox1, _
TextBox2)
End Sub
Private Sub TextBox1_Enter(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles TextBox1.Enter
yomiConv.Enabled = true
End Sub
Private Sub TextBox1_Leave(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles TextBox1.Leave
yomiConv.Enabled = false
End Sub
End Class
|
|
Formクラスの処理(上:C#、下:VB) |
このアプリケーションの実行画面は冒頭で示したとおりだ。
以上、少々コードが長くなってしまったが、行っていることはそれほど複雑ではない。ImmGetCompositionString関数は読み以外にも、変換により確定した文字列やユーザーが入力した内容(ローマ字入力なら英字の文字列)などの情報も取得できる。
カテゴリ:クラス・ライブラリ 処理対象:キーボード
カテゴリ:クラス・ライブラリ 処理対象:Win32 API
使用ライブラリ:NativeWindowクラス(System.Windows.Forms名前空間)
|
|
generated by
|
|