さまざまな型の配列のマーシャリング

配列は、同じ型の 1 つ以上の要素を含むマネージド コード内の参照型です。 配列は参照型ですが、アンマネージ関数には In パラメーターとして渡されます。 この動作は、マネージド配列がマネージド オブジェクトに渡される方法 (In/Out パラメーターとして渡される) と一致しません。 詳細については、「 コピーと固定」を参照してください。

次の表では、配列のマーシャリング オプションをリストして、それらの使用方法を説明します。

配列 説明
値による整数の。 整数の配列を In パラメーターとして渡します。
参照による整数の。 整数の配列を In/Out パラメーターとして渡します。
値による整数の (2 次元)。 整数のマトリックスを In パラメーターとして渡します。
値による文字列の。 文字列の配列を In パラメーターとして渡します。
整数による構造体の。 In パラメーターとして整数を含む構造体の配列を渡します。
文字列による構造体の。 In/Out パラメーターとして文字列のみを含む構造体の配列を渡します。 配列のメンバーを変更することができます。

このサンプルは、以下の種類の配列差を渡す法を示します。

  • 値による整数の配列。

  • サイズを変更できる、参照による整数の配列。

  • 値による整数の多次元配列 (マトリックス)。

  • 値による文字列の配列。

  • 整数による構造体の配列。

  • 文字列による構造体の配列。

配列が参照によって明示的にマーシャリングされない限り、既定の動作は、配列を In パラメーターとしてマーシャリングすることです。 この動作は、 InAttributeOutAttribute 属性を明示的に適用することで変更できます。

Arrays のサンプルで使用するアンマネージ関数とその元の関数宣言を次に示します。

  • PinvokeLib.dll からエクスポートされるTestArrayOfInts

    int TestArrayOfInts(int* pArray, int pSize);  
    
  • PinvokeLib.dll からエクスポートされるTestRefArrayOfInts

    int TestRefArrayOfInts(int** ppArray, int* pSize);  
    
  • PinvokeLib.dll からエクスポートされるTestMatrixOfInts

    int TestMatrixOfInts(int pMatrix[][COL_DIM], int row);  
    
  • PinvokeLib.dll からエクスポートされるTestArrayOfStrings

    int TestArrayOfStrings(char** ppStrArray, int size);  
    
  • PinvokeLib.dll からエクスポートされるTestArrayOfStructs

    int TestArrayOfStructs(MYPOINT* pPointArray, int size);  
    
  • PinvokeLib.dll からエクスポートされるTestArrayOfStructs2

    int TestArrayOfStructs2 (MYPERSON* pPersonArray, int size);  
    

PinvokeLib.dll はカスタム アンマネージ ライブラリであり、上記の関数および 2 つの構造体変数 MYPOINT および MYPERSONに関する実装を含んでいます。 構造体には次の要素が含まれます。

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

このサンプルでは、 MyPointMyPerson 構造体に埋め込み型が含まれています。 各メンバーが出現する順番でメモリ内に順次配列されることを保証するために、 StructLayoutAttribute 属性を設定します。

NativeMethods クラスには、 App クラスによって呼び出されるメソッドのセットが含まれます。 配列を渡す特定の方法について詳しくは、次のサンプル内のコメントを参照してください。 参照型の配列は、既定では In パラメーターとして渡されます。 呼び出し元が結果を受け取るためには、 InAttributeOutAttribute を配列が含まれる引数に明示的に適用する必要があります。

プロトタイプの宣言

// 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

関連項目