【问题标题】:Pointer of a C# object for unmanaged interop用于非托管互操作的 C# 对象的指针
【发布时间】:2015-06-28 17:34:42
【问题描述】:

我目前正在为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 我怀疑这是因为对象在对委托的调用之间被 GCed。有人能解释一下吗?

更新:更改挂载目录会消除垃圾数据,但仍然抛出异常,并且仍然在所有数据都可以使用之前

【问题讨论】:

  • 看起来有多个问题,当 GC 收集您要求 C# 编译器为 Enum 目标创建的委托对象时,您肯定会像这样崩溃。你必须存储它。明智的做法是只使用现有的 C# 包装器,您可以在 physfs 源代码分发的 extras 子目录中找到它。
  • @HansPassant 我已经检查了那个库,我决定自己写,因为那个库不满足我:D 事实上,它有点乏味和错过,例如,这个函数.,因此它对解决这个问题没有多大帮助......
  • 很难非常解释它为什么会起作用,你应该总是在回调中得到垃圾参数。当然,这很难可靠地回答。好吧,祝你好运。
  • 我想指出的是我从this MSDN article中获取了代码,所以这个方法在一定程度上必须是有效的。不过,我知道我处于边缘情况。

标签: c# delegates interop intptr physfs


【解决方案1】:

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

private static EnumFilesCallback callback = new EnumFilesCallback(Enum);

在你的主要方法中:

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

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

【讨论】:

  • 感谢您的建议,我会尽快尝试,可惜不会在 9 月之前 :(
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 2017-08-16
    • 1970-01-01
    • 1970-01-01
    • 2011-07-12
    • 2011-10-03
    • 2012-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多