コンテンツへスキップ
ものがたり
戻る

XIMとnative interopにハマるの巻

最近仕方なくWindows Formsの日本語入力まわりの実装をやっている。今までと全然違う領域だ。マルチバイト入力なんてうちの仲間は誰もやっていないので1、結局一番近いところにいるのが僕ということになってしまった。

で、Windows FormsのXplatUIX11はX11ベースで実装されているので、[DllImport(“libX11”)] で何とかしたい。となるとやはりgtk-immoduleではなくXIMということになってしまう。外野からiiimfとかには対応しないのかと有言の圧力を受けつつ(謎)、XIMである。iiimfは、誰かやってちょうだい…w

XIMクライアントなんてそんなに難しくないはずだと思うのだけど、まだ出来ていない現在まででもいろいろ罠にハマったので、その恨みつらみを書いてみる。

そもそもXIMを有効にするまで:

XCreateIC()とXVaCreateNestedList()まわりでもハマる:

XVaCreateNestedList()にstringを渡してはいけない理由は、DllImportした関数の呼び出し時に、stringがどのようにマーシャリングされるかという仕組みにある。CLIのstringは変更されてはならないから、アンマネージドコードの呼び出し前に、monoランタイム側で一時的に内容をコピーして、そのポインタを渡してやることになる2

XVaNestedListがどう実装されるかはXの実装によるが、X.Orgのx11 7.3におけるXVaCreateNestedList()の実装では、これは単なる名前へのポインタと値へのポインタの並びになっている。

さて、アンマネージドコードが終了したとき、マネージドコードから渡されたstringに対応する一時文字列は消えてしまう。monoはそのメモリ領域をいちいちfreeしたりはしないが、次のマーシャリングがあると、その領域を再利用する。この時何が起こるかというと、XVaCreateNestedList()で返されたXVaNestedListの中で示される名前へのポインタは、もう正しい名前を指さなくなってしまうということだ3

ではどうすればいいかというと、stringをGCHandleで固定してしまうこと…ではなく4、Marshal.AllocHGlobalAnsi()などを使って固定文字列のバッファを生成して、それをIntPtrとして渡せば良い。

…とりあえずこんなところだろうか。マーシャリングまわりでいろいろややこしい問題にハマったのは初めてなので、いろいろ戸惑った(というか戸惑っている)。

Footnotes

  1. まあ、数えるのであればDuncanはそうだ。

  2. 逆に、もしstringの引数がrefであれば、戻り値の文字列は場合によっては新しく生成されることにもなるが、そのインスタンス生成もアンマネージドコードの戻り値からランタイムがコピーして返している。

  3. gdbで眺めてみれば、XVaNestedListの項目名へのポインタが示す文字列が、次のマーシャリングのタイミングで変わることだろう。

  4. CLI側の文字列の内部バッファへのポインタが固定されていても、マーシャリングの一時バッファには無関係である。


この記事を共有:

前の記事
2008-04-10
次の記事
Unicode 5.1