Маршаллирование различных типов массивов

Массив является ссылочным типом в управляемом коде, содержащим один или несколько элементов одного и того же типа. Несмотря на то, что массивы являются ссылочными типами, они передаются в неуправляемые функции в виде параметров In. Это поведение не соответствует тому, как управляемые массивы передаются в управляемые объекты, которые являются параметрами in/Out. Подробнее см. в разделе Копирование и закрепление.

В следующей таблице перечислены параметры маршаллинга для массивов и описывается их использование.

Массив Description
Целые числа по значению. Передает массив целых чисел в виде параметра In.
Целые числа по ссылке. Передает массив целых чисел в виде параметра In/Out.
Целые числа по значению (двухмерный массив). Передает матрицу целых чисел в виде параметра In.
Строки по значению. Передает массив строк в виде параметра In.
Структуры с целыми числами. Передает массив структур, содержащих целые числа, в виде параметра In/Out.
Структуры со строками. Передает массив структур, содержащих только строки, в виде параметра In или Out. Элементы массива можно изменять.

Пример

В этом примере показаны способы передачи массивов следующих типов:

  • массив целых чисел по значению;

  • массив целых чисел, размер которого может быть изменен, по ссылке;

  • многомерный массив (матрица) целых чисел по значению;

  • массив строк по значению;

  • массив структур с целыми числами;

  • массив структур со строками.

Если массив явно маршалируется по ссылке, поведение по умолчанию маршалирует массив в качестве параметра In. Это поведение можно изменить, применив явным образом атрибуты InAttribute и OutAttribute .

В этом примере используются перечисленные ниже неуправляемые функции, показанные со своими исходными объявлениями.

  • ФункцияTestArrayOfInts , экспортированная из PinvokeLib.dll.

    int TestArrayOfInts(int* pArray, int pSize);  
    
  • ФункцияTestRefArrayOfInts , экспортированная из PinvokeLib.dll.

    int TestRefArrayOfInts(int** ppArray, int* pSize);  
    
  • ФункцияTestMatrixOfInts , экспортированная из PinvokeLib.dll.

    int TestMatrixOfInts(int pMatrix[][COL_DIM], int row);  
    
  • ФункцияTestArrayOfStrings , экспортированная из PinvokeLib.dll.

    int TestArrayOfStrings(char** ppStrArray, int size);  
    
  • ФункцияTestArrayOfStructs , экспортированная из PinvokeLib.dll.

    int TestArrayOfStructs(MYPOINT* pPointArray, int size);  
    
  • ФункцияTestArrayOfStructs2 , экспортированная из PinvokeLib.dll.

    int TestArrayOfStructs2 (MYPERSON* pPersonArray, int size);  
    

PinvokeLib.dll — это пользовательская неуправляемая библиотека, содержащая реализации ранее описанных функций и две переменные структуры: MYPOINT и MYPERSON. Структуры содержат следующие элементы:

typedef struct _MYPOINT  
{  
   int x;
   int y;
} MYPOINT;  
  
typedef struct _MYPERSON  
{  
   char* first;
   char* last;
} MYPERSON;  

В этом примере структуры MyPoint и MyPerson содержат внедренные типы. Чтобы гарантировать последовательное размещение членов в памяти в порядке их появления, применяется атрибут StructLayoutAttribute .

Класс NativeMethods содержит набор методов, вызываемых классом App . Конкретные сведения о передаче массивов см. в комментариях к приведенному ниже примеру. Массив, который является ссылочным типом, по умолчанию передается в виде параметра In. Чтобы вызывающий объект мог получать результаты, необходимо явным образом применить атрибуты InAttribute и OutAttribute к аргументу, содержащему массив.

Объявление прототипов

// Declares a managed structure for each unmanaged structure.
[StructLayout(LayoutKind.Sequential)]
public struct MyPoint
{
    public int X;
    public int Y;

    public MyPoint(int x, int y)
    {
        this.X = x;
        this.Y = y;
    }
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyPerson
{
    public string First;
    public string Last;

    public MyPerson(string first, string last)
    {
        this.First = first;
        this.Last = last;
    }
}

internal static class NativeMethods
{
    // Declares a managed prototype for an array of integers by value.
    // The array size cannot be changed, but the array is copied back.
    [DllImport("..\\LIB\\PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestArrayOfInts(
        [In, Out] int[] array, int size);

    // Declares a managed prototype for an array of integers by reference.
    // The array size can change, but the array is not copied back
    // automatically because the marshaler does not know the resulting size.
    // The copy must be performed manually.
    [DllImport("..\\LIB\\PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestRefArrayOfInts(
        ref IntPtr array, ref int size);

    // Declares a managed prototype for a matrix of integers by value.
    [DllImport("..\\LIB\\PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestMatrixOfInts(
        [In, Out] int[,] pMatrix, int row);

    // Declares a managed prototype for an array of strings by value.
    [DllImport("..\\LIB\\PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestArrayOfstrings(
        [In, Out] string[] stringArray, int size);

    // Declares a managed prototype for an array of structures with integers.
    [DllImport("..\\LIB\\PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestArrayOfStructs(
        [In, Out] MyPoint[] pointArray, int size);

    // Declares a managed prototype for an array of structures with strings.
    [DllImport("..\\LIB\\PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestArrayOfStructs2(
        [In, Out] MyPerson[] personArray, int size);
}
' Declares a managed structure for each unmanaged structure.
<StructLayout(LayoutKind.Sequential)>
Public Structure MyPoint
    Public x As Integer
    Public y As Integer
    Public Sub New(x As Integer, y As Integer)
        Me.x = x
        Me.y = y
    End Sub
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
Public Structure MyPerson
    Public first As String
    Public last As String
    Public Sub New(first As String, last As String)
        Me.first = first
        Me.last = last
    End Sub
End Structure

Friend Class NativeMethods
    ' Declares a managed prototype for an array of integers by value.
    ' The array size cannot be changed, but the array is copied back.
    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestArrayOfInts(
        <[In], Out> ByVal myArray() As Integer, ByVal size As Integer) _
        As Integer
    End Function

    ' Declares managed prototype for an array of integers by reference.
    ' The array size can change, but the array is not copied back 
    ' automatically because the marshaler does not know the resulting size.
    ' The copy must be performed manually.
    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestRefArrayOfInts(
        ByRef myArray As IntPtr, ByRef size As Integer) As Integer
    End Function

    ' Declares a managed prototype for a matrix of integers by value.
    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestMatrixOfInts(
        <[In], Out> ByVal matrix(,) As Integer, ByVal row As Integer) _
        As Integer
    End Function

    ' Declares a managed prototype for an array of strings by value.
    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestArrayOfStrings(
        <[In], Out> ByVal strArray() As String, ByVal size As Integer) _
        As Integer
    End Function

    ' Declares a managed prototype for an array of structures with 
    ' integers.
    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestArrayOfStructs(
        <[In], Out> ByVal pointArray() As MyPoint, ByVal size As Integer) _
        As Integer
    End Function

    ' Declares a managed prototype for an array of structures with strings.
    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestArrayOfStructs2(
        <[In], Out> ByVal personArray() As MyPerson, ByVal size As Integer) _
        As Integer
    End Function
End Class

Вызов функций

public class App
{
    public static void Main()
    {
        // array ByVal
        int[] array1 = new int[10];
        Console.WriteLine("Integer array passed ByVal before call:");
        for (int i = 0; i < array1.Length; i++)
        {
            array1[i] = i;
            Console.Write(" " + array1[i]);
        }

        int sum1 = NativeMethods.TestArrayOfInts(array1, array1.Length);
        Console.WriteLine("\nSum of elements:" + sum1);
        Console.WriteLine("\nInteger array passed ByVal after call:");

        foreach (int i in array1)
        {
            Console.Write(" " + i);
        }

        // array ByRef
        int[] array2 = new int[10];
        int size = array2.Length;
        Console.WriteLine("\n\nInteger array passed ByRef before call:");
        for (int i = 0; i < array2.Length; i++)
        {
            array2[i] = i;
            Console.Write(" " + array2[i]);
        }

        IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(size)
           * array2.Length);
        Marshal.Copy(array2, 0, buffer, array2.Length);

        int sum2 = NativeMethods.TestRefArrayOfInts(ref buffer, ref size);
        Console.WriteLine("\nSum of elements:" + sum2);
        if (size > 0)
        {
            int[] arrayRes = new int[size];
            Marshal.Copy(buffer, arrayRes, 0, size);
            Marshal.FreeCoTaskMem(buffer);
            Console.WriteLine("\nInteger array passed ByRef after call:");
            foreach (int i in arrayRes)
            {
                Console.Write(" " + i);
            }
        }
        else
        {
            Console.WriteLine("\nArray after call is empty");
        }

        // matrix ByVal
        const int DIM = 5;
        int[,] matrix = new int[DIM, DIM];

        Console.WriteLine("\n\nMatrix before call:");
        for (int i = 0; i < DIM; i++)
        {
            for (int j = 0; j < DIM; j++)
            {
                matrix[i, j] = j;
                Console.Write(" " + matrix[i, j]);
            }

            Console.WriteLine("");
        }

        int sum3 = NativeMethods.TestMatrixOfInts(matrix, DIM);
        Console.WriteLine("\nSum of elements:" + sum3);
        Console.WriteLine("\nMatrix after call:");
        for (int i = 0; i < DIM; i++)
        {
            for (int j = 0; j < DIM; j++)
            {
                Console.Write(" " + matrix[i, j]);
            }

            Console.WriteLine("");
        }

        // string array ByVal
        string[] strArray = { "one", "two", "three", "four", "five" };
        Console.WriteLine("\n\nstring array before call:");
        foreach (string s in strArray)
        {
            Console.Write(" " + s);
        }

        int lenSum = NativeMethods.TestArrayOfstrings(strArray, strArray.Length);
        Console.WriteLine("\nSum of string lengths:" + lenSum);
        Console.WriteLine("\nstring array after call:");
        foreach (string s in strArray)
        {
            Console.Write(" " + s);
        }

        // struct array ByVal
        MyPoint[] points = { new MyPoint(1, 1), new MyPoint(2, 2), new MyPoint(3, 3) };
        Console.WriteLine("\n\nPoints array before call:");
        foreach (MyPoint p in points)
        {
            Console.WriteLine($"X = {p.X}, Y = {p.Y}");
        }

        int allSum = NativeMethods.TestArrayOfStructs(points, points.Length);
        Console.WriteLine("\nSum of points:" + allSum);
        Console.WriteLine("\nPoints array after call:");
        foreach (MyPoint p in points)
        {
            Console.WriteLine($"X = {p.X}, Y = {p.Y}");
        }

        // struct with strings array ByVal
        MyPerson[] persons =
        {
            new MyPerson("Kim", "Akers"),
            new MyPerson("Adam", "Barr"),
            new MyPerson("Jo", "Brown")
        };

        Console.WriteLine("\n\nPersons array before call:");
        foreach (MyPerson pe in persons)
        {
            Console.WriteLine($"First = {pe.First}, Last = {pe.Last}");
        }

        int namesSum = NativeMethods.TestArrayOfStructs2(persons, persons.Length);
        Console.WriteLine("\nSum of name lengths:" + namesSum);
        Console.WriteLine("\n\nPersons array after call:");
        foreach (MyPerson pe in persons)
        {
            Console.WriteLine($"First = {pe.First}, Last = {pe.Last}");
        }
    }
}
Public Class App
    Public Shared Sub Main()
        ' array ByVal
        Dim array1(9) As Integer

        Console.WriteLine("Integer array passed ByVal before call:")
        Dim i As Integer
        For i = 0 To array1.Length - 1
            array1(i) = i
            Console.Write(" " & array1(i))
        Next i

        Dim sum1 As Integer = NativeMethods.TestArrayOfInts(array1, array1.Length)
        Console.WriteLine(ControlChars.CrLf & "Sum of elements:" & sum1)
        Console.WriteLine(ControlChars.CrLf & "Integer array passed ByVal after call:")
        For Each i In array1
            Console.Write(" " & i)
        Next i

        ' array ByRef
        Dim array2(9) As Integer
        Dim arraySize As Integer = array2.Length
        Console.WriteLine(ControlChars.CrLf & ControlChars.CrLf &
            "Integer array passed ByRef before call:")
        For i = 0 To array2.Length - 1
            array2(i) = i
            Console.Write(" " & array2(i))
        Next i
        Dim buffer As IntPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(
            arraySize) * array2.Length)
        Marshal.Copy(array2, 0, buffer, array2.Length)
        Dim sum2 As Integer = NativeMethods.TestRefArrayOfInts(buffer,
            arraySize)
        Console.WriteLine(ControlChars.CrLf & "Sum of elements:" & sum2)

        If arraySize > 0 Then
            Dim arrayRes(arraySize - 1) As Integer
            Marshal.Copy(buffer, arrayRes, 0, arraySize)
            Marshal.FreeCoTaskMem(buffer)

            Console.WriteLine(ControlChars.CrLf & "Integer array passed ByRef after call:")
            For Each i In arrayRes
                Console.Write(" " & i)
            Next i
        Else
            Console.WriteLine(ControlChars.CrLf & "Array after call is empty")
        End If

        ' matrix ByVal 
        Const [DIM] As Integer = 4
        Dim matrix([DIM], [DIM]) As Integer

        Console.WriteLine(ControlChars.CrLf & ControlChars.CrLf &
            "Matrix before call:")
        For i = 0 To [DIM]
            Dim j As Integer
            For j = 0 To [DIM]
                matrix(i, j) = j
                Console.Write(" " & matrix(i, j))
            Next j
            Console.WriteLine("")
        Next i

        Dim sum3 As Integer = NativeMethods.TestMatrixOfInts(matrix, [DIM] + 1)
        Console.WriteLine(ControlChars.CrLf & "Sum of elements:" & sum3)
        Console.WriteLine(ControlChars.CrLf & "Matrix after call:")
        For i = 0 To [DIM]
            Dim j As Integer
            For j = 0 To [DIM]
                Console.Write(" " & matrix(i, j))
            Next j
            Console.WriteLine("")
        Next i

        ' string array ByVal 
        Dim strArray As String() = {"one", "two", "three", "four",
            "five"}
        Console.WriteLine(ControlChars.CrLf & ControlChars.CrLf &
            "String array before call:")
        Dim s As String
        For Each s In strArray
            Console.Write(" " & s)
        Next s
        Dim lenSum As Integer = NativeMethods.TestArrayOfStrings(
            strArray, strArray.Length)
        Console.WriteLine(ControlChars.CrLf &
            "Sum of string lengths:" & lenSum)
        Console.WriteLine(ControlChars.CrLf & "String array after call:")
        For Each s In strArray
            Console.Write(" " & s)
        Next s

        ' struct array ByVal 
        Dim points As MyPoint() = {New MyPoint(1, 1), New MyPoint(2, 2),
            New MyPoint(3, 3)}
        Console.WriteLine(ControlChars.CrLf & ControlChars.CrLf &
            "Points array before call:")
        Dim p As MyPoint
        For Each p In points
            Console.WriteLine($"x = {p.x}, y = {p.y}")
        Next p
        Dim allSum As Integer = NativeMethods.TestArrayOfStructs(points,
            points.Length)
        Console.WriteLine(ControlChars.CrLf & "Sum of points:" & allSum)
        Console.WriteLine(ControlChars.CrLf & "Points array after call:")
        For Each p In points
            Console.WriteLine($"x = {p.x}, y = {p.y}")
        Next p

        ' struct with strings array ByVal 
        Dim persons As MyPerson() = {New MyPerson("Kim", "Akers"),
            New MyPerson("Adam", "Barr"),
            New MyPerson("Jo", "Brown")}
        Console.WriteLine(ControlChars.CrLf & ControlChars.CrLf &
            "Persons array before call:")
        Dim pe As MyPerson
        For Each pe In persons
            Console.WriteLine($"first = {pe.first}, last = {pe.last}")
        Next pe

        Dim namesSum As Integer = NativeMethods.TestArrayOfStructs2(persons,
            persons.Length)
        Console.WriteLine(ControlChars.CrLf & "Sum of name lengths:" &
            namesSum)
        Console.WriteLine(ControlChars.CrLf & ControlChars.CrLf _
            & "Persons array after call:")
        For Each pe In persons
            Console.WriteLine($"first = {pe.first}, last = {pe.last}")
        Next pe
    End Sub
End Class

См. также