C# で IME 操作(5)

ImmGetCompositionString() 全般についての処理。


ImmGetCompositionString() で返されるバッファは、あらかじめ null を入れてその大きさを取得して、それからバッファを確保して、もう一度呼び出して取得する。
これは、どの ImmGetCompositionString() でも同じなので、それをラップしておく。

/// <summary>
/// Marshal.AllocHGlobal で確保された IntPtr を using 文の中で使用出来るようにするためのクラス
/// </summary>
private class SafePtr : IDisposable
{
    #region IDisposable メンバ

    public void Dispose()
    {
        if (ptr != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(ptr);
            ptr = IntPtr.Zero;
            length = 0;
        }
    }

    #endregion

    private IntPtr ptr = IntPtr.Zero;
    private int length;

    public SafePtr(int size)
    {
        ptr = Marshal.AllocHGlobal(size);
        length = size;
    }

    ~SafePtr()
    {
        // 念のため……
        Dispose();
    }

    public IntPtr Ptr
    {
        get { return ptr; }
    }

    public int Length
    {
        get { return length; }
    }
}


/// <summary>
/// IMEStatic.GetCompositionString によって取得出来た情報を SafePtr にセットします。
/// </summary>
/// <param name="gcs"></param>
/// <returns>ImmGetCompositionString によって得られたデータ</returns>
private SafePtr GetCompositionString(int gcs)
{
    int length = GetCompositionString(Handle, gcs, IntPtr.Zero, 0);
    if (length == 0)
    {
        return null;
    }

    SafePtr ptr = new SafePtr(length);
    try
    {
        GetCompositionString(Handle, gcs, ptr.Ptr, length);
    }
    catch (Exception)
    {
        ptr.Dispose();
        ptr = null;
        throw;
    }

    return ptr;
}

で、ImmGetCompositionString() を2回呼び出す代わりに、この GetCompositionString() を using 文の中で使用すれば、リークの心配もない。
でも、よく考えてみれば、SafePtr で返す必要もなくて、GetCompositionString() の最後で、

byte[] buf = new byte[ptr.Length];
Marshal.Copy(ptr.Ptr, buf, 0, buf.Length);

return buf;

とかやって byte[] とかに変換して返したほうがいいのかもしれない(;´Д`)