Возврат указателей из неуправляемого в управляемый код - PullRequest
7 голосов
/ 26 февраля 2010

У меня есть неуправляемая dll, которая экспортирует следующую функцию:

SomeData* test();

Предположим SomeData как:

typedef struct _Data Data;  
struct _Data{  
    int a;  
    int b;  
}

Теперь я хочу вызвать эту функцию из кода C #. Я начинаю определять C # Struture, необходимый для пользовательского маршалинга, следующим образом:

[StructLayout(LayoutKind.Sequential)]  
public class SomeData  
{  
    public Int32 a;  
    public Int32 b;  
}  

А теперь я объявляю управляемую функцию:

[DllImport("DynamicLibrary.dll", CharSet=CharSet.Auto)]  
[return: MarshalAs(UnmanagedType.LPStruct)]  
public static extern SomeData test();  

А в основной функции у меня есть:

IntPtr ptr = test();  

Делая это, я получаю исключение MarchalDirectiveException: «Невозможно выполнить маршализацию« возвращаемого значения »: недопустимая комбинация управляемого / неуправляемого типа (Int / UInt должен быть связан с SysInt или SysUInt).

Я не выделил память для SomeData в C #, так как я ожидаю, что эта память выделена в функции C, и я бы использовал Marshal.Copy, чтобы передать ее в управляемую память.

Есть идеи? Спасибо

------------------------ Отредактировано ПОСЛЕ ОТВЕТА JaredPar ------------------- -

На самом деле, я допустил ошибку при копировании кода на мой вопрос. Реальная управляемая подпись, которую я использовал, была:

[DllImport ("DynamicLibrary.dll", CharSet = CharSet.Auto)]
[возвращение: MarshalAs (UnmanagedType.LPStruct)]
public static extern IntPtr test ();

Ответ JaredPar по-прежнему актуален. Чтобы получить правильное поведение, у меня есть 2 варианта:

1) Используйте 'public static extern IntPtr test ();' (без атрибута MarshalAs) подпись, а затем доступ к возвращенному указателю, как предложил JaredPar.

2) Использовать 'public static extern SomeData test ();' (с атрибутом MarshalAs), а затем просто используйте SomeData sd = test ();

1 Ответ

10 голосов
/ 26 февраля 2010

При объявлении управляемой функции необходимо сопоставить типы указателей со ссылочными значениями или IntPtr значениями. В этом случае модификатор LPStruct не поможет. Самое простое решение - преобразовать возвращаемое значение test в IntPtr, а не SomeData, поскольку нативный метод возвращает значение указателя. Затем вы можете написать следующую оболочку

[DllImport("DynamicLibrary.dll", CharSet=CharSet.Auto)]
public static extern IntPtr test();

public static SomeData testWrapper() {
  var ptr = test();
  try {
    return (SomeData)Marshal.PtrToStructure(ptr, typeof(SomeData));
  } finally {
    // Free the pointer here if it's allocated memory
  }
}
...