IME関連

Aleaさんのとこから(http://d.hatena.ne.jp/alea/20050715#p1)。
コンソールアプリ中でウィンドウハンドルを渡すという話があったので、コンソールウィンドウにユニークなタイトルを付けてFindWindowでハンドルを入手してみたけど、Imm系の関数呼び出しは失敗してしまった。ウィンドウハンドルを渡せばいいというものではないみたい。(^^;

ふと思い立ってImm系の関数をC#から呼んでみた。(無駄にC#2.0で組んでいます)

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

unsafe public struct CANDIDATELIST
{
    public uint dwSize;
    public uint dwStyle;
    public uint dwCount;
    public uint dwSelection;
    public uint dwPageStart;
    public uint dwPageSize;
    public fixed uint dwOffset[1];
}

public enum GCL : uint
{
    CONVERSION = 0x0001,
    REVERSECONVERSION = 0x0002,
    REVERSE_LENGTH = 0x0003,
}

unsafe public class Imm
{
    [DllImport("imm32")]
    public extern static IntPtr ImmGetContext(IntPtr hWnd);
    [DllImport("imm32")]
    public extern static bool ImmReleaseContext(IntPtr hWnd, IntPtr hIMC);
    [DllImport("imm32")]
    public extern static uint ImmGetConversionList(IntPtr hKL, IntPtr hIMC,
        string src, sbyte lpDst, uint dwBufLen, GCL uFlag);
}

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

    [DllImport("user32")]
    internal extern static IntPtr GetKeyboardLayout(uint idThread);

    static void GetConversionList(IntPtr hWnd, string src, List<string> dst)
    {
        IntPtr hIMC = Imm.ImmGetContext(hWnd);
        IntPtr hKL = GetKeyboardLayout(0);
        uint dwSize = Imm.ImmGetConversionList(hKL, hIMC, src, null, 0, GCL.REVERSECONVERSION);
        sbyte buf = new sbyte[dwSize];
        Imm.ImmGetConversionList(hKL, hIMC, src, buf, dwSize, GCL.REVERSECONVERSION);
        unsafe
        {
            fixed (sbyte* p = &buf[0])
            {
                CANDIDATELIST* lpCand = (CANDIDATELIST*)p;
                for (uint i = 0; i < lpCand->dwCount; ++i)
                    dst.Add(new string(p + lpCand->dwOffset[i]));
            }
        }
        Imm.ImmReleaseContext(hWnd, hIMC);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        List<string> dst = new List<string>();
        GetConversionList(this.Handle, "明日", dst);
        foreach (string s in dst)
            MessageBox.Show(s);
    }
}

P/Invokeで文字列と構造体のポインタを渡すところはstringとsbyte[]でいいのですが、戻ってきた後でunsafeになるのがちょっと嫌。Marshalクラス使ってマネージドオブジェクトに詰め直せばなんとかなるけど、それも効率悪いし。