Указатель на объект C# для неуправляемого взаимодействия

В настоящее время я пишу оболочку для библиотеки 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 раза с достоверными данными, в пятый раз - с мусорными данными, а затем он вызывает исключение AccessViolationException. Я подозреваю, что это происходит потому, что объект получает GCed между вызовами делегата. Кто-нибудь может пролить свет на это?

ОБНОВЛЕНИЕ: Изменение смонтированного каталога устраняет мусорные данные, но исключение все еще выдается, и еще до того, как все данные могут быть использованы

2 ответа

Решение

Спасибо всем, кто потратил свое время, пытаясь дать ответ! Я наконец нашел источник проблемы и решил ее!

Проблема была... мне немного стыдно за это... созыв соглашения. Все методы PInvoked были объявлены как cdecl, а я забыл объявить делегатов как таковые, поэтому он создал несбалансированные стеки, хаос и еще много чего...

Вы пытались создать обратный вызов и сохранить его как статическое поле класса?

private static EnumFilesCallback callback = new EnumFilesCallback(Enum);

И в вашем основном методе:

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

Вероятно, следует избегать, чтобы GC собирал локальную переменную, содержащую объект делегата.

Другие вопросы по тегам