用于非托管互操作的 C# 对象的指针
Pointer of a C# object for unmanaged interop
我目前正在为 PhysFS library, and I stumbled across a bit of troubles regarding the marshalling of managed objects. Take for example the 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 次,第五次它包含垃圾数据,然后抛出 AccessViolationException
我怀疑这是因为对象在对委托的调用之间得到了 GC。任何人都可以阐明这一点吗?
更新:更改挂载目录,垃圾数据消除,但仍抛出异常,仍未消费完所有数据
您是否尝试创建回调并将其存储为 class 静态字段?
private static EnumFilesCallback callback = new EnumFilesCallback(Enum);
并且在您的主要方法中:
PhysFS.EnumerateFilesCallback("/hello/", callback, x);
这应该可以避免 GC 收集持有委托对象的局部变量。
感谢所有花时间尝试提供答案的人!我终于找到问题的根源并解决了!
问题是……我有点惭愧……调用约定。
所有 PInvoked 方法都被声明为 cdecl 而我忘记这样声明委托,所以它创建了不平衡的堆栈和混乱等等......
我目前正在为 PhysFS library, and I stumbled across a bit of troubles regarding the marshalling of managed objects. Take for example the 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 次,第五次它包含垃圾数据,然后抛出 AccessViolationException 我怀疑这是因为对象在对委托的调用之间得到了 GC。任何人都可以阐明这一点吗?
更新:更改挂载目录,垃圾数据消除,但仍抛出异常,仍未消费完所有数据
您是否尝试创建回调并将其存储为 class 静态字段?
private static EnumFilesCallback callback = new EnumFilesCallback(Enum);
并且在您的主要方法中:
PhysFS.EnumerateFilesCallback("/hello/", callback, x);
这应该可以避免 GC 收集持有委托对象的局部变量。
感谢所有花时间尝试提供答案的人!我终于找到问题的根源并解决了!
问题是……我有点惭愧……调用约定。 所有 PInvoked 方法都被声明为 cdecl 而我忘记这样声明委托,所以它创建了不平衡的堆栈和混乱等等......