Как преобразовать собственную память C в структуру C #?

#c# #interop #pinvoke

#c# #взаимодействие #pinvoke

Вопрос:

У меня есть следующий собственный функциональный интерфейс на C :

 int func1(void* param, int sizeOfParam).
  

В документации приведен следующий пример вызова:

 typedef struct
{
    int x;
    int y;
    int width;
    int height;
} Rect;

Rect rect;

int func1((void*)amp;rect, sizeof(rect));
  

Мне нужно вызвать эту функцию из кода C #.

У меня есть следующий заголовок в C # от разработчиков собственной библиотеки:

 [DllImport(NATIVE_DLL_NAME, 
 CallingConvention = CallingConvention.Cdecl, 
 EntryPoint = "func1")]
private static extern int func1(IntPtr param, int sizeOfParam);
  

У меня также есть следующая структура C # Rect :

 public struct Rect
{
    int x;
    int y;
    int width;
    int height;
};
  

Мне нужно вызвать func1 код C # и передать Rect :

Я делаю следующее:

 Rect rect = new Rect();
int rectSize = System.Runtime.InteropServices.Marshal.SizeOf(rect);

func1(???, rectSize);
  

Что поместить в положение ??? , в котором rect должно быть передано (но это невозможно из-за несовместимых типов)?

Кажется, это IntPtr должно быть передано, а затем преобразовано в struct rect . Как этого добиться?

( rect здесь является выходным параметром)

Обновить:

Желательно не изменять подписи кода C и оболочек C # — это третья часть кода.
Более того, не всегда переменная of Rect передается как первый параметр func1

Ответ №1:

Вы изменили правила игры, чтобы запретить внесение изменений в код C #. И поэтому P / invoke должен иметь такую форму:

 private static extern int func1(IntPtr param, int sizeOfParam);
  

В этом случае вам нужно выполнить сортировку вручную:

 int size = Marshal.SizeOf(typeof(Rect));
IntPtr param1 = Marshal.AllocHGlobal(size);
try
{
    func1(param1, size);
    Rect rect = (Rect)Marshal.PtrToStructure(param1, typeof(Rect));
}
finally
{
    Marshal.FreeHGlobal(param1);
}
  

Комментарии:

1. Спасибо! Извините за путаницу. Просто небольшой вопрос. Может ли отсутствие [StructLayout(LayoutKind.Sequential)] in Rect потенциально вызвать проблемы?

2. Вам нужно использовать [StructLayout(LayoutKind.Sequential)] . И какие проблемы вы видите?

3. На данный момент проблем нет. Спасибо. Я только что прочитал на msdn.microsoft.com/en-us/library/0t2cwe11(v=vs.71).aspx определение StructLayout и боялся, что компилятор может переставлять члены struct по своему усмотрению. И если сейчас это не проблема, это не значит, что в будущем она не появится.

Ответ №2:

Я бы, вероятно, немного упростил себе жизнь, используя out параметр типа Rect , а не IntPtr . Вот так:

 [StructLayout(LayoutKind.Sequential)]
public struct Rect
{
    int x;
    int y;
    int width;
    int height;
};

[DllImport(NATIVE_DLL_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "func1")]
private static extern int func1(out Rect param, int sizeOfParam);
  

Затем для вызова функции вы можете написать это:

 Rect param;
int res = func1(out param, Marshal.SizeOf(typeof(Rect)));
  

Комментарии:

1. Да, вы действительно упростили жизнь 🙂 Странно, что это не всегда Rect передается как первый параметр…

2. Вы можете создавать func1Rect и func1SomeOtherType т. Д. чтобы справиться с этим.

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

Ответ №3:

Попробуйте вместо этого передать ref Rect .

 [DllImport(NATIVE_DLL_NAME, CallingConvention = CallingConvention.Cdecl, EntryPoint = "func1")]
private static extern int func1(ref Rect param, int sizeOfParam);
  

Комментарии:

1. Это не совсем то, о чем я спрашиваю. Я прояснил вопрос.