Marshalling różne typy tablic
Tablica jest typem referencyjnym w kodzie zarządzanym, który zawiera co najmniej jeden element tego samego typu. Mimo że tablice są typami referencyjnymi, są przekazywane jako parametry W do funkcji niezarządzanych. To zachowanie jest niespójne ze sposobem przekazywania zarządzanych tablic do zarządzanych obiektów, co jest parametrami wywłaszanymi/wychodzącymi. Aby uzyskać dodatkowe informacje, zobacz Kopiowanie i przypinanie.
W poniższej tabeli wymieniono opcje marshalingu dla tablic i opisano ich użycie.
Tablica | opis |
---|---|
Liczba całkowita według wartości. | Przekazuje tablicę liczb całkowitych jako parametr In. |
Liczba całkowita według odwołania. | Przekazuje tablicę liczb całkowitych jako parametr in/out. |
Liczby całkowite według wartości (dwuwymiarowe). | Przekazuje macierz liczb całkowitych jako parametr In. |
Ciągi według wartości. | Przekazuje tablicę ciągów jako parametr In. |
Struktury z liczbami całkowitymi. | Przekazuje tablicę struktur, które zawierają liczby całkowite jako parametr In. |
Struktury z ciągami. | Przekazuje tablicę struktur, które zawierają tylko ciągi jako parametr in/out. Elementy członkowskie tablicy można zmienić. |
Przykład
W tym przykładzie pokazano, jak przekazać następujące typy tablic:
Tablica liczb całkowitych według wartości.
Tablica liczb całkowitych według odwołania, które można zmienić.
Tablica wielowymiarowa (macierz) liczb całkowitych według wartości.
Tablica ciągów według wartości.
Tablica struktur z liczbami całkowitymi.
Tablica struktur z ciągami.
Jeśli tablica nie jest jawnie marshaled przez odwołanie, domyślne zachowanie marshaluje tablicę jako parametr In. To zachowanie można zmienić, stosując InAttribute jawnie atrybuty i OutAttribute .
W przykładzie Arrays użyto następujących funkcji niezarządzanych pokazanych przy użyciu oryginalnej deklaracji funkcji:
TestArrayOfInts wyeksportowane z PinvokeLib.dll.
int TestArrayOfInts(int* pArray, int pSize);
TestRefArrayOfInts wyeksportowane z PinvokeLib.dll.
int TestRefArrayOfInts(int** ppArray, int* pSize);
TestMatrixOfInts wyeksportowane z PinvokeLib.dll.
int TestMatrixOfInts(int pMatrix[][COL_DIM], int row);
TestArrayOfStrings wyeksportowane z PinvokeLib.dll.
int TestArrayOfStrings(char** ppStrArray, int size);
TestArrayOfStructs wyeksportowane z PinvokeLib.dll.
int TestArrayOfStructs(MYPOINT* pPointArray, int size);
TestArrayOfStructs2 wyeksportowane z PinvokeLib.dll.
int TestArrayOfStructs2 (MYPERSON* pPersonArray, int size);
PinvokeLib.dll to niestandardowa niezarządzana biblioteka zawierająca implementacje poprzednio wymienionych funkcji i dwie zmienne struktury, MYPOINT i MYPERSON. Struktury zawierają następujące elementy:
typedef struct _MYPOINT
{
int x;
int y;
} MYPOINT;
typedef struct _MYPERSON
{
char* first;
char* last;
} MYPERSON;
W tym przykładzie MyPoint
struktury i MyPerson
zawierają typy osadzone. Atrybut StructLayoutAttribute jest ustawiony tak, aby upewnić się, że elementy członkowskie są rozmieszczone sekwencyjnie w pamięci, w kolejności, w jakiej się pojawiają.
Klasa NativeMethods
zawiera zestaw metod wywoływanych przez klasę App
. Aby uzyskać szczegółowe informacje na temat przekazywania tablic, zobacz komentarze w poniższym przykładzie. Tablica, która jest typem odwołania, jest domyślnie przekazywana jako parametr In. Aby obiekt wywołujący otrzymał wyniki, należy jawnie zastosować atrybuty InAttribute i OutAttribute do argumentu zawierającego tablicę.
Deklarowanie prototypów
// 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
Wywoływanie funkcji
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