Передача структуры в С++ API с использованием Marshal.StructureToPtr в С#

Я использую API, написанный на С++ в своем коде (написание в С#). API требует параметра "Указатель на структуру". Структура состоит из "Int" и Char Массивы: например

 unsafe public struct ToBePassed 
    { 
        Int32 Num1;
        Int32 Num2; 
        Char[] Data; // or fixed Char Data[255];
    }

Я не могу напрямую передать указатель структуры API, потому что в этом случае я получаю ошибку, поскольку "Указатели не могут ссылаться на структуры маршалистов". Код успешно скомпилирован, но эта ошибка возникает, когда я выполняю (отлаживаю) код.

Теперь у меня есть два варианта: 1-й: - Передача структуры по ссылке: Я хочу спросить, должен ли API, требующий, чтобы указатель на структуру мог получить адрес, когда я передаю структуру по ссылке. Обратите внимание, что API вернет данные в "Char [] Data".

2nd: - Использование Marshal.StructureToPtr: Это преобразует Структурный указатель в IntPtr. Опять Сомнение такое же, будет ли этот API правильно принят?

Спасибо за ваше время!

С уважением, Swanand

Ответы

Ответ 1

Если для этого требуется только указатель, вы можете выделить некоторую неуправляемую память, упорядочить структуру в памяти и передать этот указатель на свою функцию. Затем после этого вы можете вернуться к структуре (если хотите) и освободить память. Прежде чем вы производите все, вам нужно правильно определить структуру. Что-то вроде этого:

[StructLayout(
    LayoutKind.Sequential,      //must specify a layout
    CharSet = CharSet.Ansi)]    //if you intend to use char
public struct ToBePassed
{
    public Int32 Num1;
    public Int32 Num2;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
    public Char[] Data;    //specify the size using MarshalAs
}

[DllImport("...")]
public static extern void APICall(IntPtr argPtr);


public static void CallFunction(ToBePassed managedObj)
{
    IntPtr unmanagedAddr = Marshal.AllocHGlobal(Marshal.SizeOf(managedObj));

    Marshal.StructureToPtr(managedObj, unmanagedAddr, true);

    APICall(unmanagedAddr);

    Marshal.PtrToStructure(unmanagedAddr, managedObj);

    Marshal.FreeHGlobal(unmanagedAddr);
    unmanagedAddr = IntPtr.Zero;
}

[править]
Чтобы имитировать массивы переменной длины, выделите неуправляемую память в структуре и инициализируйте, как обычно.

[StructLayout(LayoutKind.Sequential)]
public struct SomeStruct
{
    public Int32 X;
    public Int32 Y;
}

[StructLayout(LayoutKind.Sequential)]
public struct VLA
{
    public Int32 intArrayLength;
    public Int32 SomeStructArrayLength;
    public IntPtr intArray;
    public IntPtr SomeStructArray;
}

public static VLA CreateVLA(int[] intArray, SomeStruct[] SomeStructArray)
{
    var vla = new VLA()
    {
        intArrayLength = intArray.Length,
        SomeStructArrayLength = SomeStructArray.Length,
        intArray = Marshal.AllocHGlobal(intArray.Length * Marshal.SizeOf(typeof(int))),
        SomeStructArray = Marshal.AllocHGlobal(SomeStructArray.Length * Marshal.SizeOf(typeof(SomeStruct))),
    };
    Marshal.Copy(intArray, 0, vla.intArray, intArray.Length);
    //there no overload to copy arbitrary arrays, do it manually
    for (int i = 0; i < SomeStructArray.Length; i++)
    {
        Marshal.StructureToPtr(
            SomeStructArray[i],
            vla.SomeStructArray + i * Marshal.SizeOf(typeof(SomeStruct)),
            true);
    }
    return vla;
}