C++で書かれたDLLにポインタを渡したいことがあります。例えば次のような関数がエクスポートされていたとします。
void WINAPI ConvertToShort(char* pstr, short* pret);
これをC#側から使用したい。char* は文字列なので C# 側からは string を渡してやるだけでよいですが、short* については何を渡していいものやら困ります。
C#には IntPtr という型があります。これは汎用的なポインタを表す型で、ほぼ void* と同義です。
ただしC#は超厳しい型付け言語なので、void* みたいな万能選手は万能ゆえの曖昧さを解決するために、回りくどい変換メソッドを経由しないと使えません。
具体的には、IntPtrの変数に Marshal.AllocHGlobalで必要なサイズのメモリを確保し、それをC++のDLLに渡します。
さらにMarshal.ReadInt16(必要な型によって異なる)などで変換後、確保したメモリをMarshal.FreeHGlobalで解放する、と3段階の面倒なプロセスを経なければいけません。
C#はその厳しさゆえテキトーなC++とは違いメモリ破壊など厄介な問題が発生しませんが、こういうときに手続きが面倒です。リスクと面倒さはトレードオフですね。
//usingではこれが必要 using System.Runtime.InteropServices; //ここはクラス内で定義 [DllImport("DrsUtil.dll", EntryPoint = "ConvertToShort")] extern static void ConvertToShort(string pstr, IntPtr pret); //ここは関数内の記述 IntPtr buffer = new IntPtr(); buffer = Marshal.AllocHGlobal(2); //2バイトのメモリ確保 ConvertToShort("XXXXXX", buffer); short sval = Marshal.ReadInt16(buffer); //変換 Marshal.FreeHGlobal(buffer); //メモリ解放
実用的には、次のようにConvertToShortをラップして使いやすい形に整えるのが賢明でしょう。
[DllImport("DrsUtil.dll", EntryPoint = "ConvertToShort")] extern static void _ConvertToShort(string pstr, IntPtr pret); //呼び出し元の名前変えちゃう short ConvertToShort(string str) { IntPtr buffer = new IntPtr(); buffer = Marshal.AllocHGlobal(2); _ConvertToShort(str, buffer); short sval = Marshal.ReadInt16(buffer); Marshal.FreeHGlobal(buffer); return sval; }
IntPtrには何でも入りますから、例えば構造体をゲットすることも可能です。ただしC#側で構造体をC#流に定義してあげなければなりません。若干面倒です。
参考
IntPtr からの色々な型への変換 – Kenrowの覚書と日々
@IT:.NET TIPS Win32 APIやDLL関数に構造体を渡すには? – C#