私は現在PhysFS ライブラリのラッパーを書いていますが、管理対象オブジェクトのマーシャリングに関してちょっとした問題に出くわしました。たとえば、PHYSFS_enumerateFilesCallbackメソッドを取り上げます。このメソッドは、関数ポインターとユーザー定義のポインターを引数として取ります。管理対象オブジェクトをこのメソッドに渡すにはどうすればよいですか? これは私が現在行っていることです:
// This is the delegate signature
public delegate void EnumFilesCallback(IntPtr data, string origdir, string fname);
// This is the method signature
[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void PHYSFS_enumerateFilesCallback(string dir, EnumFilesCallback c, IntPtr d);
最後に、これは私がメソッドに任意のオブジェクトを渡すために行っていることです:
// I use the unsafe keyword because the whole Interop class is declared so.
// This code was taken from https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle(VS.71).aspx
public static void EnumerateFilesCallback(string dir, EnumFilesCallback c, object data)
{
unsafe
{
GCHandle objHandle = GCHandle.Alloc(data);
Interop.PHYSFS_enumerateFilesCallback(dir, c, (IntPtr)objHandle);
objHandle.Free();
}
}
このコードを実行すると:
static void Enum(IntPtr d, string origdir, string fname )
{
System.Runtime.InteropServices.GCHandle handle = (System.Runtime.InteropServices.GCHandle)d;
TestClass c = (TestClass)handle.Target;
Console.WriteLine("{0} {1}", origdir, fname);
}
static void Main(string[] args)
{
PhysFS.Init("");
PhysFS.Mount("D:\\", "/hello", true);
TestClass x = new TestClass() { a = 3, b = 4 }; // This can be any gibberish object
PhysFS.EnumerateFilesCallback("/hello/", Enum, x);
}
デリゲートは正当なデータで 4 回呼び出され、5 回目にガベージ データが含まれてから AccessViolationException がスローされます。これは、オブジェクトがデリゲートの呼び出しの間に GC されるためだと思われます。誰でもこれに光を当てることができますか?
更新: マウントされたディレクトリを変更するとゴミデータが削除されますが、例外は引き続きスローされ、すべてのデータが消費される前にまだ発生します。