非托管互操作的C#对象的指针

时间:2015-06-28 17:34:43

标签: c# delegates interop intptr physfs

我正在为PhysFS library编写一个包装器,我偶然发现了有关托管对象编组的一些麻烦。以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。有人能说清楚这个吗?

更新:更改已安装的目录会消除垃圾数据,但仍然会抛出异常,并且仍然可以使用所有数据

2 个答案:

答案 0 :(得分:0)

您是否尝试过创建回调并将其存储为类静态字段?

private static EnumFilesCallback callback = new EnumFilesCallback(Enum);

在你的主要方法中:

PhysFS.EnumerateFilesCallback("/hello/", callback, x);

这应该可以避免GC收集持有委托对象的局部变量。

答案 1 :(得分:0)

感谢所有投入时间的人试图提供答案!我终于找到了问题的根源并解决了它!

问题是......我有点惭愧...... 调用约定。 所有PInvoked方法都声明为 cdecl ,而我忘了声明委托这样,所以它创建了不平衡的堆栈和混乱等等......