char*をStringBuilder型として取得してました。
これはこれで実績があったのですが、
つい最近期待した動きになっていないことが
発覚しました。
⇒と前フリしてみましたが、実は文末にあるように
単なるミスだった説が浮上していたり・・・(^-^;)
まぁ色んな方法があるぞ、ということで(汗
●現象
例えば、TestAPI.dllのTESTFUNCという関数が以下のような
プロトタイプ宣言だとします。
void __stdcall TESTFUNC( int* buf_max_size, char* buf, int* buf_len );
上記をC#で使う際、今までは以下のように宣言してました。
using System;
using System.Runtime.InteropServices;
using System.Text;
(略)
// bufはStringBuilder型で取得する
[DllImport("TestAPI.dll", CharSet = CharSet.Auto)]
private static extern void TESTFUNC(
ref int buf_max_size,
[MarshalAs(UnmanagedType.LPStr)] StringBuilder buf,
ref int buf_len
);
TESTFUNCは文字列の末尾をスペースで埋めてから返しています。
buf_max_size=10で"abc"という文字列を返す場合、
buf="abc "と末尾にスペースが入って返ってきます。
VisualStudio.NET 2005でしか試していませんが、
上記のソースだとDebugビルド時のみ期待どおり動作しました。
Releaseビルドで実行した場合は、bufの末尾のスペースが
一部化けてしまい、Trimでのブランクカットが期待どおりに
動いていませんでした。
●対応
原因は深く追っていませんが、文字のバイト数の違いとか
そのあたりが怪しい気がしてます。
で、そもそも悩む必要はなくて、C言語っぽく扱えば良いのでは?
という当たり前の結論に達しました。
C言語のcharは1バイト・・・すなわちC#言語のbyte型です。
そこで、以下のようにbyte型の配列で受け取るように
ソースを修正しました。
using System;
using System.Runtime.InteropServices;
(略)
// bufはbyte[]型で取得する
[DllImport("TestAPI.dll", CharSet = CharSet.Auto)]
private static extern void TESTFUNC(
ref int buf_max_size,
[MarshalAs(UnmanagedType.LPArray)] byte[] buf,
ref int buf_len
);
当たり前ではありますが、この対応でばっちりでした。
もしbyte[]を文字列で見たいのであれば、以下のような
ソースで簡単に確認できます。
using System.Text;
(略)
Console.WriteLine(Encoding.ASCII.GetString(buf));
まだまだ最後に頼るのはバイト列・・・なことを実感
した一件なのでした。
と、ここまで書いて、念のためネットで調べてみました。
http://www.atmarkit.co.jp/fdotnet/dotnettips/025w32string/w32string.html
確かにStringBuilder型で受け取っているようです。
ふと、DllImport時にMarshalAsしてないことに気づきました。
自分、[MarshalAs(UnmanagedType.LPStr)] StringBuilderとしてたけど、
もしかして余計なお世話だった・・・?
ってかオイラの宣言ミスなだけ??
でも、もういいや。byte[]最強の方向で。


