none
テキストボックスで漢字名を入力した際にルビを振る方法について RRS feed

  • 質問

  • こんにちは。アべです。

    テキストボックスで漢字名を入力した際に
    隣に仮名入力用のテキストボックスを
    配置しておいて、そこに漢字名のルビを
    表示させたいのですが、可能でしょうか?

    可能であればやり方のキーワードを
    教えていただけないでしょうか?

    すみませんが、宜しくお願い致します。

    「ルビ」や「振り仮名」で検索をかけて
    見たのですが、上手く見つけることが出来ませんでした。

    ---環境
    環境:C#2.0
    画面:WinForm
    以上

     

    2006年6月7日 0:09

すべての返信

  • 「猫プロ」 のこれ以降の章がヒントになるのでしゃないでしょうか。

    第278章 IMEの操作 その1

    NCL 2.0 に、対話する方法が存在していたらすごい...

    2006年6月7日 0:48
  •  じゃんぬねっと さんからの引用

    「猫プロ」 のこれ以降の章がヒントになるのでしゃないでしょうか。

    第278章 IMEの操作 その1


    それを .NET的に利用した一例
    「GDNJ」-「振り仮名の取得」のキャッシュ

    # 「GDNJ」の掲示板って、もう見れなくなっているのですね。
    # 2006/06末までではなかったのかな?

    2006年6月7日 3:34
  • 有難うございます。
    頂いたページを元に実装が出来ました。

    しかし、たまに正しく動かないことがあります。

    たとえば、「協賛」といれると「キョウサン」となったりします。

    どうも、文字を打ってスペース⇒Enterを行うと上手く入力できない場合があるようです。原因と対処方法がわからず困っております。

    何か対処方法がご存知の方、アドバイスをお願い致します。

    ---ソース
    using System;
    using System.ComponentModel;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;

    namespace WindowsApplication
    {
     public partial class ATextBox : TextBox
     {
      public ATextBox()
      {
       InitializeComponent();
      }

      public ATextBox(IContainer container)
      {
       container.Add(this);

       InitializeComponent();
      }

     
      const int WM_IME_COMPOSITION = 0x010F;
            const int GCS_RESULTREADSTR = 0x0200;

            [DllImport("Imm32.dll")]
            public static extern int ImmGetContext(IntPtr hWnd);
            [DllImport("Imm32.dll")]
            public static extern int ImmGetCompositionString(
                int hIMC,
                int dwIndex,
                StringBuilder lpBuf,
                int dwBufLen
                );
            [DllImport("Imm32.dll")]
            public static extern bool ImmReleaseContext(IntPtr hWnd,int hIMC);

            [Serializable]
            public class CompositionEventArgs:EventArgs
            {
                public string ImeStr;
            }

            public delegate void CompositionEventHandler(object sender,CompositionEventArgs e);
            public event CompositionEventHandler CompositionEvent;
        
      protected override void WndProc(ref Message m)
            {
                if (CompositionEvent != null && m.Msg == WM_IME_COMPOSITION)
                {
        if (((int)m.LParam & GCS_RESULTREADSTR) > 0)
                    {     
         int Imc = ImmGetContext(this.Handle);
                        int sz = ImmGetCompositionString(Imc,GCS_RESULTREADSTR,null,0);     
         StringBuilder str = new StringBuilder(sz);
                        ImmGetCompositionString(Imc,GCS_RESULTREADSTR,str,str.Capacity);
                        ImmReleaseContext(this.Handle,Imc);
                        CompositionEventArgs args = new CompositionEventArgs();
                        args.ImeStr = str.ToString();
                        CompositionEvent(this,args);
                    }
                }
                base.WndProc(ref m);
            }
        }
    }

     

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;

    namespace WindowsApplication
    {
     public partial class Form1 : Form
     {
      public Form1()
      {
       InitializeComponent();
      }

      private void aTextBox1_CompositionEvent(object sender, ATextBox.CompositionEventArgs e)
      {
       _label.Text = e.ImeStr;
      }
     }
    }

     

    2006年6月7日 5:09
  • ImmGetCompositionString は Unicode 版でもバイト単位でバッファサイズを、NULL 文字を含めない分で返すそうですから、その辺の問題ですかね。

    StringBuilder の代わりに [OutAttribute] byte[] を渡すように定義して、sz 調べるために一度呼び出した後、(NULL 文字含めた)byte[sz + 1] を確保。再呼び出しでバッファ長には sz + 1 を指定。Encoding.Default.GetString で 0 から sz バイト分を文字列に変換……と言った手順になりますかね。

    // どうも終端文字をまじめに書き込んでくれないっぽいな……。

    ちなみに、この辺の関数は現在のウィンドウが使用している IME が実装を提供するので、結果が同じではないかも知れません(半角カナで返すのか全角かなで返すのかは IME 次第という事)。

    2006年6月7日 7:24
  • とっちゃんです。

    終端の'\0'追加は、IMEのバージョンによって異なります。なので、APIの戻り値の書き込みバイト数を見てちゃんと\0を自分でセットしないと C形式の文字列になりません。

    あと、IMEによってフリガナを全角文字で返すものと半角文字で返すものがあります。

    MS-IMEだけで見てもバージョンによって異なりますので、わりとはまりやすい部分ですね。

    細かい制御をする場合は、原則、IMEのバージョンごとに状況が違うのが前提と思ってないと絶対にはまります。

    前はOKだったのになんていうのは全く通じませんから(^^;

    2006年6月7日 7:40
  • とっちゃんさん有難うございます。
    もう少し調べてみて駄目なら、下記の対応にします。

    1.ロジックを半角と全角のアルファベットのみを
        フィルタして取得し、全角なら半角に変換します。

    理由:こちらでは、終端に'\0'がつくだけではなく、
    mやcやrなどのアルファベットもつく場合があるので、
    上記の対応ですませようかと考えております。

    他に良い方法があればよいのですが、、、

    2006年6月7日 7:52
  • 過去に私が格闘した奇跡が・・・・・

    スレッド: 振り仮名取得でゴミが入る
    http://72.14.209.104/search?q=cache:uAPYrDPxmG0J:www.gdncom.jp/general/bbs/ShowPost.aspx%3FPostID%3D21415+%E3%81%82%E3%82%8A%E3%81%8C%E3%81%A8%E3%81%86%E3%81%94%E3%81%96%E3%81%84%E3%81%BE%E3%81%97%E3%81%9F%E3%80%80out+IntPtr+lpBuf+site:www.gdncom.jp&hl=ja&gl=jp&ct=clnk&cd=1

    この2ページ目がどうしても検索できない・・・Orz

    もっともこの2ページの結論コードから少し変えた気もします。
    ただ、いずれにしても振り仮名を実現して、現在、業務で動いています。
    今、出張中なのでそのコードを示せませんが、明日の夜、遅くともあさって(たぶん、あさっての午前中になると思います)にはご紹介できると思います。

    2006年6月7日 9:56
    モデレータ
  • とっちゃんです。

    Hongliang さんも書いているように、ImmGetCompositionString APIの戻り値を見てきちんと処理しなければなりません。

    実際にいろいろやってみるとわかりますが、ゴミに当たるところに「かな」が入ってくることもあります。

    なので、きちんと API の戻り値から有効文字列長を受けてバッファを処理する必要があります。

    面倒な気がするとは思いますが、それが現実ですので...(^^;

    2006年6月7日 10:12
  • こんにちは。アべです。

    trapemiyaさん、とっちゃんさん、有難うございます。
    非常に感謝しております。

    この対応でIME 2002や2003のナチュラルやスタンダード
    などを使用して、格闘してみてごみ文字の特性から、
    ロジックで回避が無理だという結論に至りました。

    アドバイスの通り、trapemiya さんのサンプルを期待しつつ、
    これから正しい実装を模索します。

    本当に有難うございます。

    2006年6月8日 1:25
  • では、昔作成したヘタレコードを掲載します。昔作ったコードですので、いろいろ突っ込みどころはあるかもしれませんが、動いているコードなので変に直すよりそのまま掲載します。特に、変な風にふりがなが出るという報告は受けていないので、業務で問題なく使用できていると思うのですが、何かあればフィードバックをお願いします。
    
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Drawing;
    using System.Data;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    using System.Text;
    
    namespace WindowsControlLibrary
    {
        /// <summary>
        /// FuriganaTextBox の概要の説明です。
        /// </summary>
        public class FuriganaTextBox : System.Windows.Forms.TextBox
        {
            /// <summary>
            /// 必要なデザイナ変数です。
            /// </summary>
            private System.ComponentModel.Container components = null;
    
            const int WM_IME_COMPOSITION = 0x010F;
            const int GCS_RESULTREADSTR = 0x0200;
    
            [DllImport("Imm32.dll", CharSet = CharSet.Auto)]
            public static extern int ImmGetContext(IntPtr hWnd);
    
    /*
    //        
    //         * CharSet = CharSet.Autoにすると、ここから返される読みの文字数が、
    //         * WinXpではその文字数の2倍になってしまう。省略すると、CharSet.Ansi
    //         
    //        [DllImport("Imm32.dll", CharSet = CharSet.Auto)]
            [DllImport("Imm32.dll")]
            public static extern int ImmGetCompositionString(
                int hIMC,
                int dwIndex,
                StringBuilder lpBuf,
                int dwBufLen 
                );
    */
            [DllImport("Imm32.dll")]
            public static extern int ImmGetCompositionString(
                int hIMC,
                int dwIndex,
                IntPtr lpBuf,
                int dwBufLen 
                );
            
            [DllImport("Imm32.dll", CharSet = CharSet.Auto)]
            public static extern bool ImmReleaseContext(IntPtr hWnd,int hIMC);
    
            [Serializable]
                public class CompositionEventArgs:EventArgs
            {
                public string ImeStr;
            }
    
            #region デリゲート & イベントの定義
            //デリゲート & イベントの定義
            public delegate void CompositionEventHandler(object sender,CompositionEventArgs e);
            public event CompositionEventHandler CompositionEvent;
            #endregion
    
            #region コンストラクタ & Dispose
            //コンストラクタ
            public FuriganaTextBox()
            {
                // この呼び出しは、Windows.Forms フォーム デザイナで必要です。
                InitializeComponent();
    
                // TODO: InitComponent 呼び出しの後に初期化処理を追加してください。
            }
    
            /// <summary>
            /// 使用されているリソースに後処理を実行します。
            /// </summary>
            protected override void Dispose( bool disposing )
            {
                if( disposing )
                {
                    if( components != null )
                        components.Dispose();
                }
                base.Dispose( disposing );
            }
            #endregion
    
            #region コンポーネント デザイナで生成されたコード 
            /// <summary>
            /// デザイナ サポートに必要なメソッドです。このメソッドの内容を 
            /// コード]エディタで変更しないでください。
            /// </summary>
            private void InitializeComponent()
            {
                components = new System.ComponentModel.Container();
            }
            #endregion
    
            protected override void OnPaint(PaintEventArgs pe)
            {
                // TODO: カスタム描画コードをここに追加してください。
    
                // OnPaint で基本クラスを呼び出し中
                base.OnPaint(pe);
            }
    
            #region WndProc
            protected override void WndProc(ref Message m)
            {
                if (CompositionEvent != null && m.Msg == WM_IME_COMPOSITION)
                {
                    if (((int)m.LParam & GCS_RESULTREADSTR)>0)
                    {
                        IntPtr buffer = IntPtr.Zero;
    
                        try
                        {
                            int Imc = ImmGetContext(this.Handle);
                            int bytes = ImmGetCompositionString(Imc, GCS_RESULTREADSTR, IntPtr.Zero, 0);
    
                            buffer = Marshal.AllocHGlobal(bytes);
                            ImmGetCompositionString(Imc, GCS_RESULTREADSTR, buffer, bytes);
                            byte[] arrByte = new Byte[bytes];
                            Marshal.Copy(buffer, arrByte, 0, bytes);
                            string str = Encoding.GetEncoding(932).GetString(arrByte);
    
                            ImmReleaseContext(this.Handle,Imc);
    
                            CompositionEventArgs args = new CompositionEventArgs();
                            args.ImeStr = str;
    
                            CompositionEvent(this,args);
                        }
                        finally
                        {
                            Marshal.FreeHGlobal(buffer);
                        }
                    }
    
    /*うまく動かず。                
                    if (((int)m.LParam & GCS_RESULTREADSTR)>0)
                    {
                        IntPtr buffer = IntPtr.Zero;
    
                        try
                        {
                            int Imc = ImmGetContext(this.Handle);
                            int bytes = ImmGetCompositionString(Imc, GCS_RESULTREADSTR, IntPtr.Zero, 0);
    
                            buffer = Marshal.AllocHGlobal(bytes);
                            ImmGetCompositionString(Imc, GCS_RESULTREADSTR, buffer, bytes);
                            string str = Marshal.PtrToStringAnsi(buffer);
    
                            ImmReleaseContext(this.Handle,Imc);
    
                            CompositionEventArgs args = new CompositionEventArgs();
                            args.ImeStr = str;
    
                            CompositionEvent(this,args);
                        }
                        finally
                        {
                            Marshal.FreeHGlobal(buffer);
                        }
                    }
    */
    
    /*
                    if (((int)m.LParam & GCS_RESULTREADSTR)>0)
                    {
                        int Imc = ImmGetContext(this.Handle);
                        int sz = ImmGetCompositionString(Imc,GCS_RESULTREADSTR,null,0);
                        StringBuilder str = new StringBuilder(sz);
                        ImmGetCompositionString(Imc,GCS_RESULTREADSTR,str,str.Capacity);
                        ImmReleaseContext(this.Handle,Imc);
                        CompositionEventArgs args = new CompositionEventArgs();        
    
    //             * ImmGetCompositionStringをCharSet = CharSet.AnsiでP/Invokeするようにしたので、
    //             * 以下のOSによる切り分けは必要なくなった。
                
    //                    switch (System.Environment.OSVersion.Platform)
    //                    {
    //                        case PlatformID.Win32NT:
    //                            args.ImeStr = str.ToString().Substring(0, sz / 2);
    //                            break;
    //                        case PlatformID.Win32S:
    //                            args.ImeStr = str.ToString().Substring(0, sz);
    //                            break;
    //                        case PlatformID.Win32Windows:
    //                            args.ImeStr = str.ToString().Substring(0, sz);
    //                            break;
    //                        default:
    //                            args.ImeStr = str.ToString().Substring(0, sz);
    //                            break;
    //                    }
                        args.ImeStr = str.ToString().Substring(0, sz);
                    
                        CompositionEvent(this,args);
                    }
    */
                }
                
                base.WndProc(ref m);
            }
            #endregion
    
        }
    }
    
    
    2006年6月9日 1:01
    モデレータ