Маршалинг классов, структур и объединенийMarshaling Classes, Structures, and Unions

Классы и структуры в .NET Framework похожи.Classes and structures are similar in the .NET Framework. И те и другие могут иметь поля, свойства и события.Both can have fields, properties, and events. Они также могут иметь статические и нестатические методы.They can also have static and nonstatic methods. Примечательным отличием является то, что структуры являются типами значений, а классы — ссылочными типами.One notable difference is that structures are value types and classes are reference types.

В таблице ниже представлены варианты маршалинга классов, структур и объединений, описывается их применение и приводятся ссылки на соответствующие примеры вызова неуправляемого кода.The following table lists marshaling options for classes, structures, and unions; describes their usage; and provides a link to the corresponding platform invoke sample.

ТипType ОписаниеDescription ОбразецSample
Класс по значению.Class by value. Передает класс с целочисленными членами в качестве параметра In или Out (как и в случае управляемого класса).Passes a class with integer members as an In/Out parameter, like the managed case. Пример СистимеSysTime sample
Структура по значению.Structure by value. Передает структуры в качестве параметров In.Passes structures as In parameters. Пример структурStructures sample
Структура по ссылке.Structure by reference. Передает структуры в качестве параметров In и Out.Passes structures as In/Out parameters. Пример OSInfoOSInfo sample
Структура с вложенными структурами (выровненная).Structure with nested structures (flattened). Передает класс, представляющий структуру с вложенными структурами, в неуправляемую функцию.Passes a class that represents a structure with nested structures in the unmanaged function. Структура выравнивается в одну большую структуру в управляемом прототипе.The structure is flattened to one big structure in the managed prototype. Пример ФиндфилеFindFile sample
Структура с указателем на другую структуру.Structure with a pointer to another structure. Передает структуру, содержащую указатель на другую структуру в качестве члена.Passes a structure that contains a pointer to a second structure as a member. Пример структурStructures Sample
Массив структур с целочисленными значениями по значению.Array of structures with integers by value. Передает массив структур, содержащих только целые числа, в виде параметра In или Out.Passes an array of structures that contain only integers as an In/Out parameter. Элементы массива можно изменять.Members of the array can be changed. Пример массивовArrays Sample
Массив структур с целочисленными значениями и строками по ссылке.Array of structures with integers and strings by reference. Передает массив структур, содержащих целые числа и строки, в качестве параметра Out.Passes an array of structures that contain integers and strings as an Out parameter. Вызываемая функция выделяет память под массив.The called function allocates memory for the array. Пример АутаррайофструктсOutArrayOfStructs Sample
Объединения с типами значений.Unions with value types. Передает объединения с типами значений (целочисленными и двойной точности).Passes unions with value types (integer and double). Пример объединенийUnions sample
Объединения со смешанными типами.Unions with mixed types. Передает объединения со смешанными типами (целое число и строка).Passes unions with mixed types (integer and string). Пример объединенийUnions sample
Значения Null в структуре.Null values in structure. Передает пустую ссылку (Nothing в Visual Basic) вместо ссылки на тип значения.Passes a null reference (Nothing in Visual Basic) instead of a reference to a value type. Пример HandleRefHandleRef sample

Пример структурStructures sample

В этом примере показан способ передачи структуры, указывающей на вторую структуру, передачи структуры с внедренной структурой и передачи структуры с внедренным массивом.This sample demonstrates how to pass a structure that points to a second structure, pass a structure with an embedded structure, and pass a structure with an embedded array.

В примере используются следующие неуправляемые функции, показанные с исходными объявлениями:The Structs sample uses the following unmanaged functions, shown with their original function declaration:

  • функция TestStructInStruct, экспортированная из PinvokeLib.dll;TestStructInStruct exported from PinvokeLib.dll.

    int TestStructInStruct(MYPERSON2* pPerson2);
    
  • функция TestStructInStruct3, экспортированная из PinvokeLib.dll;TestStructInStruct3 exported from PinvokeLib.dll.

    void TestStructInStruct3(MYPERSON3 person3);
    
  • функция TestArrayInStruct, экспортированная из PinvokeLib.dll.TestArrayInStruct exported from PinvokeLib.dll.

    void TestArrayInStruct(MYARRAYSTRUCT* pStruct);
    

PinvokeLib.dll — это пользовательская неуправляемая библиотека, которая содержит реализации перечисленных выше функций и четыре структуры: MYPERSON, MYPERSON2, MYPERSON3 и MYARRAYSTRUCT.PinvokeLib.dll is a custom unmanaged library that contains implementations for the previously listed functions and four structures: MYPERSON, MYPERSON2, MYPERSON3, and MYARRAYSTRUCT. Эти структуры содержат следующие элементы:These structures contain the following elements:

typedef struct _MYPERSON
{
   char* first;
   char* last;
} MYPERSON, *LP_MYPERSON;

typedef struct _MYPERSON2
{
   MYPERSON* person;
   int age;
} MYPERSON2, *LP_MYPERSON2;

typedef struct _MYPERSON3
{
   MYPERSON person;
   int age;
} MYPERSON3;

typedef struct _MYARRAYSTRUCT
{
   bool flag;
   int vals[ 3 ];
} MYARRAYSTRUCT;

Управляемые структуры MyPerson, MyPerson2, MyPerson3 и MyArrayStruct обладают следующими характеристиками:The managed MyPerson, MyPerson2, MyPerson3, and MyArrayStruct structures have the following characteristic:

  • Структура MyPerson содержит только члены-строки.MyPerson contains only string members. Поле CharSet задает формат строк ANSI при передаче строки в неуправляемую функцию.The CharSet field sets the strings to ANSI format when passed to the unmanaged function.

  • MyPerson2 содержит указатель IntPtr на структуру MyPerson.MyPerson2 contains an IntPtr to the MyPerson structure. Тип IntPtr заменяет исходный указатель на неуправляемую структуру, так как приложения .NET Framework не используют указатели, если код не помечен как небезопасный.The IntPtr type replaces the original pointer to the unmanaged structure because .NET Framework applications do not use pointers unless the code is marked unsafe.

  • Структура MyPerson3 содержит структуру MyPerson в качестве внедренной.MyPerson3 contains MyPerson as an embedded structure. Внедренную структуру можно выровнять путем помещения ее элементов прямо в основную структуру, или ее можно оставить внедренной, как показано в примере.A structure embedded within another structure can be flattened by placing the elements of the embedded structure directly into the main structure, or it can be left as an embedded structure, as is done in this sample.

  • Структура MyArrayStruct содержит массив целочисленных значений.MyArrayStruct contains an array of integers. Атрибут MarshalAsAttribute задает для перечисления UnmanagedType значение ByValArray, которое используется для указания количества элементов в массиве.The MarshalAsAttribute attribute sets the UnmanagedType enumeration value to ByValArray, which is used to indicate the number of elements in the array.

Для всех структур в этом примере применяется атрибут StructLayoutAttribute, гарантирующий последовательное размещение элементов в памяти в порядке их появления.For all structures in this sample, the StructLayoutAttribute attribute is applied to ensure that the members are arranged in memory sequentially, in the order in which they appear.

Класс NativeMethods содержит управляемые прототипы методов TestStructInStruct, TestStructInStruct3 и TestArrayInStruct, вызываемые классом App.The NativeMethods class contains managed prototypes for the TestStructInStruct, TestStructInStruct3, and TestArrayInStruct methods called by the App class. Каждый прототип объявляет один параметр указанным ниже способом.Each prototype declares a single parameter, as follows:

  • TestStructInStruct объявляет в качестве своего параметра ссылку на тип MyPerson2.TestStructInStruct declares a reference to type MyPerson2 as its parameter.

  • TestStructInStruct3 в качестве своего параметра объявляет тип MyPerson3 и передает параметр по значению.TestStructInStruct3 declares type MyPerson3 as its parameter and passes the parameter by value.

  • TestArrayInStruct объявляет в качестве своего параметра ссылку на тип MyArrayStruct.TestArrayInStruct declares a reference to type MyArrayStruct as its parameter.

Структуры, выступающие в роли аргументов методов, передаются по значению, если параметр не содержит ключевого слова ref (ByRef в Visual Basic).Structures as arguments to methods are passed by value unless the parameter contains the ref (ByRef in Visual Basic) keyword. Например, метод TestStructInStruct передает в неуправляемый код ссылку (значение адреса) на объект типа MyPerson2.For example, the TestStructInStruct method passes a reference (the value of an address) to an object of type MyPerson2 to unmanaged code. Для работы со структурой, на которую указывает MyPerson2, в примере с помощью методов Marshal.AllocCoTaskMem и Marshal.SizeOf создается буфер заданного размера и возвращается его адрес.To manipulate the structure that MyPerson2 points to, the sample creates a buffer of a specified size and returns its address by combining the Marshal.AllocCoTaskMem and Marshal.SizeOf methods. Далее в неуправляемый буфер копируется содержимое управляемой структуры.Next, the sample copies the content of the managed structure to the unmanaged buffer. Наконец, используются метод Marshal.PtrToStructure для маршалинга данных из неуправляемого буфера в управляемый объект и метод Marshal.FreeCoTaskMem для освобождения неуправляемого блока памяти.Finally, the sample uses the Marshal.PtrToStructure method to marshal data from the unmanaged buffer to a managed object and the Marshal.FreeCoTaskMem method to free the unmanaged block of memory.

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

// Declares a managed structure for each unmanaged structure.
[StructLayout(LayoutKind::Sequential, CharSet = CharSet::Ansi)]
public value struct MyPerson
{
public:
    String^ first;
    String^ last;
};

[StructLayout(LayoutKind::Sequential)]
public value struct MyPerson2
{
public:
    IntPtr person;
    int age;
};

[StructLayout(LayoutKind::Sequential)]
public value struct MyPerson3
{
public:
    MyPerson person;
    int age;
};

[StructLayout(LayoutKind::Sequential)]
public value struct MyArrayStruct
{
public:
    bool flag;
    [MarshalAs(UnmanagedType::ByValArray, SizeConst = 3)]
    array<int>^ vals;
};

private ref class NativeMethods
{
public:
    // Declares a managed prototype for unmanaged function.
    [DllImport("..\\LIB\\PinvokeLib.dll")]
    static int TestStructInStruct(MyPerson2% person2);

    [DllImport("..\\LIB\\PinvokeLib.dll")]
    static int TestStructInStruct3(MyPerson3 person3);

    [DllImport("..\\LIB\\PinvokeLib.dll")]
    static int TestArrayInStruct(MyArrayStruct% myStruct);
};
// Declares a managed structure for each unmanaged structure.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyPerson
{
    public string first;
    public string last;
}

[StructLayout(LayoutKind.Sequential)]
public struct MyPerson2
{
    public IntPtr person;
    public int age;
}

[StructLayout(LayoutKind.Sequential)]
public struct MyPerson3
{
    public MyPerson person;
    public int age;
}

[StructLayout(LayoutKind.Sequential)]
public struct MyArrayStruct
{
    public bool flag;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public int[] vals;
}

internal static class NativeMethods
{
    // Declares a managed prototype for unmanaged function.
    [DllImport("..\\LIB\\PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestStructInStruct(ref MyPerson2 person2);

    [DllImport("..\\LIB\\PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestStructInStruct3(MyPerson3 person3);

    [DllImport("..\\LIB\\PinvokeLib.dll", CallingConvention = CallingConvention.Cdecl)]
    internal static extern int TestArrayInStruct(ref MyArrayStruct myStruct);
}
' Declares a managed structure for each unmanaged structure.
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
Public Structure MyPerson
    Public first As String
    Public last As String
End Structure

<StructLayout(LayoutKind.Sequential)>
Public Structure MyPerson2
    Public person As IntPtr
    Public age As Integer
End Structure

<StructLayout(LayoutKind.Sequential)>
Public Structure MyPerson3
    Public person As MyPerson
    Public age As Integer
End Structure

<StructLayout(LayoutKind.Sequential)>
Public Structure MyArrayStruct
    Public flag As Boolean
    <MarshalAs(UnmanagedType.ByValArray, SizeConst:=3)>
    Public vals As Integer()
End Structure

Friend Class NativeMethods
    ' Declares managed prototypes for unmanaged functions.
    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestStructInStruct(
        ByRef person2 As MyPerson2) As Integer
    End Function

    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestStructInStruct3(
        ByVal person3 As MyPerson3) As Integer
    End Function

    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Function TestArrayInStruct(
        ByRef myStruct As MyArrayStruct) As Integer
    End Function
End Class

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

public ref class App
{
public:
    static void Main()
    {
        // Structure with a pointer to another structure.
        MyPerson personName;
        personName.first = "Mark";
        personName.last = "Lee";

        MyPerson2 personAll;
        personAll.age = 30;

        IntPtr buffer = Marshal::AllocCoTaskMem(Marshal::SizeOf(personName));
        Marshal::StructureToPtr(personName, buffer, false);

        personAll.person = buffer;

        Console::WriteLine("\nPerson before call:");
        Console::WriteLine("first = {0}, last = {1}, age = {2}",
            personName.first, personName.last, personAll.age);

        int res = NativeMethods::TestStructInStruct(personAll);

        MyPerson personRes =
            (MyPerson)Marshal::PtrToStructure(personAll.person,
                MyPerson::typeid);

        Marshal::FreeCoTaskMem(buffer);

        Console::WriteLine("Person after call:");
        Console::WriteLine("first = {0}, last = {1}, age = {2}",
            personRes.first, personRes.last, personAll.age);

        // Structure with an embedded structure.
        MyPerson3 person3;// = gcnew MyPerson3();
        person3.person.first = "John";
        person3.person.last = "Evans";
        person3.age = 27;
        NativeMethods::TestStructInStruct3(person3);

        // Structure with an embedded array.
        MyArrayStruct myStruct;// = new MyArrayStruct();

        myStruct.flag = false;
        myStruct.vals = gcnew array<int>(3);
        myStruct.vals[0] = 1;
        myStruct.vals[1] = 4;
        myStruct.vals[2] = 9;

        Console::WriteLine("\nStructure with array before call:");
        Console::WriteLine(myStruct.flag);
        Console::WriteLine("{0} {1} {2}", myStruct.vals[0],
            myStruct.vals[1], myStruct.vals[2]);

        NativeMethods::TestArrayInStruct(myStruct);
        Console::WriteLine("\nStructure with array after call:");
        Console::WriteLine(myStruct.flag);
        Console::WriteLine("{0} {1} {2}", myStruct.vals[0],
            myStruct.vals[1], myStruct.vals[2]);
    }
};
public class App
{
    public static void Main()
    {
        // Structure with a pointer to another structure.
        MyPerson personName;
        personName.first = "Mark";
        personName.last = "Lee";

        MyPerson2 personAll;
        personAll.age = 30;

        IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(personName));
        Marshal.StructureToPtr(personName, buffer, false);

        personAll.person = buffer;

        Console.WriteLine("\nPerson before call:");
        Console.WriteLine("first = {0}, last = {1}, age = {2}",
            personName.first, personName.last, personAll.age);

        int res = NativeMethods.TestStructInStruct(ref personAll);

        MyPerson personRes =
            (MyPerson)Marshal.PtrToStructure(personAll.person,
            typeof(MyPerson));

        Marshal.FreeCoTaskMem(buffer);

        Console.WriteLine("Person after call:");
        Console.WriteLine("first = {0}, last = {1}, age = {2}",
            personRes.first, personRes.last, personAll.age);

        // Structure with an embedded structure.
        MyPerson3 person3 = new MyPerson3();
        person3.person.first = "John";
        person3.person.last = "Evans";
        person3.age = 27;
        NativeMethods.TestStructInStruct3(person3);

        // Structure with an embedded array.
        MyArrayStruct myStruct = new MyArrayStruct();

        myStruct.flag = false;
        myStruct.vals = new int[3];
        myStruct.vals[0] = 1;
        myStruct.vals[1] = 4;
        myStruct.vals[2] = 9;

        Console.WriteLine("\nStructure with array before call:");
        Console.WriteLine(myStruct.flag);
        Console.WriteLine("{0} {1} {2}", myStruct.vals[0],
            myStruct.vals[1], myStruct.vals[2]);

        NativeMethods.TestArrayInStruct(ref myStruct);
        Console.WriteLine("\nStructure with array after call:");
        Console.WriteLine(myStruct.flag);
        Console.WriteLine("{0} {1} {2}", myStruct.vals[0],
            myStruct.vals[1], myStruct.vals[2]);
    }
}
Public Class App
    Public Shared Sub Main()
        ' Structure with a pointer to another structure.
        Dim personName As MyPerson
        personName.first = "Mark"
        personName.last = "Lee"

        Dim personAll As MyPerson2
        personAll.age = 30

        Dim buffer As IntPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(
            personName))
        Marshal.StructureToPtr(personName, buffer, False)

        personAll.person = buffer

        Console.WriteLine(ControlChars.CrLf & "Person before call:")
        Console.WriteLine("first = {0}, last = {1}, age = {2}",
            personName.first, personName.last, personAll.age)

        Dim res As Integer = NativeMethods.TestStructInStruct(personAll)

        Dim personRes As MyPerson =
            CType(Marshal.PtrToStructure(personAll.person,
            GetType(MyPerson)), MyPerson)

        Marshal.FreeCoTaskMem(buffer)

        Console.WriteLine("Person after call:")
        Console.WriteLine("first = {0}, last = {1}, age = {2}",
        personRes.first,
            personRes.last, personAll.age)

        ' Structure with an embedded structure.
        Dim person3 As New MyPerson3()
        person3.person.first = "John"
        person3.person.last = "Evans"
        person3.age = 27
        NativeMethods.TestStructInStruct3(person3)

        ' Structure with an embedded array.
        Dim myStruct As New MyArrayStruct()

        myStruct.flag = False
        Dim array(2) As Integer
        myStruct.vals = array
        myStruct.vals(0) = 1
        myStruct.vals(1) = 4
        myStruct.vals(2) = 9

        Console.WriteLine(vbNewLine + "Structure with array before call:")
        Console.WriteLine(myStruct.flag)
        Console.WriteLine("{0} {1} {2}", myStruct.vals(0),
            myStruct.vals(1), myStruct.vals(2))

        NativeMethods.TestArrayInStruct(myStruct)
        Console.WriteLine(vbNewLine + "Structure with array after call:")
        Console.WriteLine(myStruct.flag)
        Console.WriteLine("{0} {1} {2}", myStruct.vals(0),
            myStruct.vals(1), myStruct.vals(2))
    End Sub
End Class

пример FindFileFindFile sample

В этом примере показан способ передачи структуры, содержащей другую, внедренную структуру, в неуправляемую функцию.This sample demonstrates how to pass a structure that contains a second, embedded structure to an unmanaged function. Также показано, как с помощью атрибута MarshalAsAttribute объявить массив фиксированной длины внутри структуры.It also demonstrates how to use the MarshalAsAttribute attribute to declare a fixed-length array within the structure. В этом примере элементы внедренной структуры добавляются в родительскую структуру.In this sample, the embedded structure elements are added to the parent structure. Пример внедренной структуры (не преобразованной в плоскую структуру) см. в разделе Пример структур.For a sample of an embedded structure that is not flattened, see Structures Sample.

В примере FindFile используются следующие неуправляемые функции, показанные с исходными объявлениями:The FindFile sample uses the following unmanaged function, shown with its original function declaration:

  • функция FindFirstFile, экспортированная из Kernel32.dll.FindFirstFile exported from Kernel32.dll.

    HANDLE FindFirstFile(LPCTSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData);
    

Исходная структура, переданная в функцию, содержит следующие элементы:The original structure passed to the function contains the following elements:

typedef struct _WIN32_FIND_DATA
{
  DWORD    dwFileAttributes;
  FILETIME ftCreationTime;
  FILETIME ftLastAccessTime;
  FILETIME ftLastWriteTime;
  DWORD    nFileSizeHigh;
  DWORD    nFileSizeLow;
  DWORD    dwReserved0;
  DWORD    dwReserved1;
  TCHAR    cFileName[ MAX_PATH ];
  TCHAR    cAlternateFileName[ 14 ];
} WIN32_FIND_DATA, *PWIN32_FIND_DATA;

В этом примере класс FindData содержит члены данных, соответствующие каждому элементу исходной и внедренной структур.In this sample, the FindData class contains a corresponding data member for each element of the original structure and the embedded structure. Вместо двух исходных символьных буферов класс подставляет строки.In place of two original character buffers, the class substitutes strings. Атрибут MarshalAsAttribute задает для перечисления UnmanagedType значение ByValTStr, которое используется для идентификации встроенных символьных массивов фиксированной длины в неуправляемых структурах.MarshalAsAttribute sets the UnmanagedType enumeration to ByValTStr, which is used to identify the inline, fixed-length character arrays that appear within the unmanaged structures.

Класс NativeMethods содержит управляемый прототип метода FindFirstFile, передающий класс FindData в качестве параметра.The NativeMethods class contains a managed prototype of the FindFirstFile method, which passes the FindData class as a parameter. Этот параметр должен быть объявлен с атрибутами InAttribute и OutAttribute, так как классы, являющиеся ссылочными типами, по умолчанию передаются как параметры In.The parameter must be declared with the InAttribute and OutAttribute attributes because classes, which are reference types, are passed as In parameters by default.

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

// Declares a class member for each structure element.
[StructLayout(LayoutKind::Sequential, CharSet = CharSet::Auto)]
public ref class FindData
{
public:
    int  fileAttributes;
    // creationTime was an embedded FILETIME structure.
    int  creationTime_lowDateTime;
    int  creationTime_highDateTime;
    // lastAccessTime was an embedded FILETIME structure.
    int  lastAccessTime_lowDateTime;
    int  lastAccessTime_highDateTime;
    // lastWriteTime was an embedded FILETIME structure.
    int  lastWriteTime_lowDateTime;
    int  lastWriteTime_highDateTime;
    int  nFileSizeHigh;
    int  nFileSizeLow;
    int  dwReserved0;
    int  dwReserved1;
    [MarshalAs(UnmanagedType::ByValTStr, SizeConst = 260)]
    String^  fileName;
    [MarshalAs(UnmanagedType::ByValTStr, SizeConst = 14)]
    String^  alternateFileName;
};

private ref class NativeMethods
{
public:
    // Declares a managed prototype for the unmanaged function.
    [DllImport("Kernel32.dll", CharSet = CharSet::Auto)]
    static IntPtr FindFirstFile(String^ fileName, [In, Out]
        FindData^ findFileData);
};
// Declares a class member for each structure element.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class FindData
{
    public int fileAttributes = 0;
    // creationTime was an embedded FILETIME structure.
    public int creationTime_lowDateTime = 0;
    public int creationTime_highDateTime = 0;
    // lastAccessTime was an embedded FILETIME structure.
    public int lastAccessTime_lowDateTime = 0;
    public int lastAccessTime_highDateTime = 0;
    // lastWriteTime was an embedded FILETIME structure.
    public int lastWriteTime_lowDateTime = 0;
    public int lastWriteTime_highDateTime = 0;
    public int nFileSizeHigh = 0;
    public int nFileSizeLow = 0;
    public int dwReserved0 = 0;
    public int dwReserved1 = 0;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
    public string fileName = null;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
    public string alternateFileName = null;
}

internal static class NativeMethods
{
    // Declares a managed prototype for the unmanaged function.
    [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
    internal static extern IntPtr FindFirstFile(
        string fileName, [In, Out] FindData findFileData);
}
' Declares a class member for each structure element.
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
Public Class FindData
    Public fileAttributes As Integer = 0
    ' creationTime was a by-value FILETIME structure.
    Public creationTime_lowDateTime As Integer = 0
    Public creationTime_highDateTime As Integer = 0
    ' lastAccessTime was a by-value FILETIME structure.
    Public lastAccessTime_lowDateTime As Integer = 0
    Public lastAccessTime_highDateTime As Integer = 0
    ' lastWriteTime was a by-value FILETIME structure.
    Public lastWriteTime_lowDateTime As Integer = 0
    Public lastWriteTime_highDateTime As Integer = 0
    Public nFileSizeHigh As Integer = 0
    Public nFileSizeLow As Integer = 0
    Public dwReserved0 As Integer = 0
    Public dwReserved1 As Integer = 0
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)>
    Public fileName As String = Nothing
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=14)>
    Public alternateFileName As String = Nothing
End Class

Friend Class NativeMethods
    ' Declares a managed prototype for the unmanaged function.
    Friend Declare Auto Function FindFirstFile Lib "Kernel32.dll" (
        ByVal fileName As String, <[In], Out> ByVal findFileData As _
        FindData) As IntPtr
End Class

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

public ref class App
{
public:
    static void Main()
    {
        FindData^ fd = gcnew FindData();
        IntPtr handle = NativeMethods::FindFirstFile("C:\\*.*", fd);
        Console::WriteLine("The first file: {0}", fd->fileName);
    }
};
public class App
{
    public static void Main()
    {
        FindData fd = new FindData();
        IntPtr handle = NativeMethods.FindFirstFile("C:\\*.*", fd);
        Console.WriteLine($"The first file: {fd.fileName}");
    }
}
Public Class App
    Public Shared Sub Main()
        Dim fd As New FindData()
        Dim handle As IntPtr = NativeMethods.FindFirstFile("C:\*.*", fd)
        Console.WriteLine($"The first file: {fd.fileName}")
    End Sub
End Class

пример UnionsUnions sample

В этом примере показан способ передачи структур, содержащих только типы значений, а также структур, содержащих тип значения и строку, в качестве параметров в неуправляемую функцию, ожидающую объединения.This sample demonstrates how to pass structures containing only value types, and structures containing a value type and a string as parameters to an unmanaged function expecting a union. Объединение представляет собой ячейку памяти, которая может совместно использоваться двумя и более переменными.A union represents a memory location that can be shared by two or more variables.

В примере используются следующие неуправляемые функции, показанные с исходными объявлениями:The Unions sample uses the following unmanaged function, shown with its original function declaration:

  • функция TestUnion, экспортированная из PinvokeLib.dll.TestUnion exported from PinvokeLib.dll.

    void TestUnion(MYUNION u, int type);
    

PinvokeLib.dll — это пользовательская неуправляемая библиотека, содержащая реализацию указанной выше функции и два объединения: MYUNION и MYUNION2.PinvokeLib.dll is a custom unmanaged library that contains an implementation for the previously listed function and two unions, MYUNION and MYUNION2. Объединение содержит следующие элементы:The unions contain the following elements:

union MYUNION
{
    int number;
    double d;
}

union MYUNION2
{
    int i;
    char str[128];
};

В управляемом коде объединения определяются как структуры.In managed code, unions are defined as structures. Структура MyUnion в качестве своих членов содержит два типа значений: целочисленный и число двойной точности.The MyUnion structure contains two value types as its members: an integer and a double. Для управления точным положением каждого члена данных задается атрибут StructLayoutAttribute.The StructLayoutAttribute attribute is set to control the precise position of each data member. Атрибут FieldOffsetAttribute предоставляет физическое положение полей внутри неуправляемого представления объединения.The FieldOffsetAttribute attribute provides the physical position of fields within the unmanaged representation of a union. Обратите внимание, что значения смещения у обоих членов одинаковы, что позволяет членам определять один и тот же участок памяти.Notice that both members have the same offset values, so the members can define the same piece of memory.

Объединения MyUnion2_1 и MyUnion2_2 содержат тип значения (целое число) и строку соответственно.MyUnion2_1 and MyUnion2_2 contain a value type (integer) and a string, respectively. В управляемом коде типы значений и ссылочные типы не могут перекрываться.In managed code, value types and reference types are not permitted to overlap. Чтобы при вызове одной и той же неуправляемой функции вызывающий объект мог использовать оба типа, в этом примере применяется перегрузка метода.This sample uses method overloading to enable the caller to use both types when calling the same unmanaged function. Структура MyUnion2_1 задана явным образом и имеет точное значение смещения.The layout of MyUnion2_1 is explicit and has a precise offset value. Напротив, MyUnion2_2 имеет последовательную структуру, так как структуры, заданные явным образом, нельзя использовать со ссылочными типами.In contrast, MyUnion2_2 has a sequential layout, because explicit layouts are not permitted with reference types. Атрибут MarshalAsAttribute задает для перечисления UnmanagedType значение ByValTStr, которое используется для идентификации встроенных символьных массивов фиксированной длины в неуправляемом представлении объединения.The MarshalAsAttribute attribute sets the UnmanagedType enumeration to ByValTStr, which is used to identify the inline, fixed-length character arrays that appear within the unmanaged representation of the union.

Класс NativeMethods содержит прототипы для методов TestUnion и TestUnion2.The NativeMethods class contains the prototypes for the TestUnion and TestUnion2 methods. TestUnion2 перегружается для объявления MyUnion2_1 или MyUnion2_2 в качестве параметров.TestUnion2 is overloaded to declare MyUnion2_1 or MyUnion2_2 as parameters.

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

// Declares managed structures instead of unions.
[StructLayout(LayoutKind::Explicit)]
public value struct MyUnion
{
public:
    [FieldOffset(0)]
    int i;
    [FieldOffset(0)]
    double d;
};

[StructLayout(LayoutKind::Explicit, Size = 128)]
public value struct MyUnion2_1
{
public:
    [FieldOffset(0)]
    int i;
};

[StructLayout(LayoutKind::Sequential)]
public value struct MyUnion2_2
{
public:
    [MarshalAs(UnmanagedType::ByValTStr, SizeConst = 128)]
    String^ str;
};

private ref class NativeMethods
{
public:
    // Declares managed prototypes for unmanaged function.
    [DllImport("..\\LIB\\PInvokeLib.dll")]
    static void TestUnion(MyUnion u, int type);

    [DllImport("..\\LIB\\PInvokeLib.dll")]
    static void TestUnion2(MyUnion2_1 u, int type);

    [DllImport("..\\LIB\\PInvokeLib.dll")]
    static void TestUnion2(MyUnion2_2 u, int type);
};
// Declares managed structures instead of unions.
[StructLayout(LayoutKind.Explicit)]
public struct MyUnion
{
    [FieldOffset(0)]
    public int i;
    [FieldOffset(0)]
    public double d;
}

[StructLayout(LayoutKind.Explicit, Size = 128)]
public struct MyUnion2_1
{
    [FieldOffset(0)]
    public int i;
}

[StructLayout(LayoutKind.Sequential)]
public struct MyUnion2_2
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string str;
}

internal static class NativeMethods
{
    // Declares managed prototypes for unmanaged function.
    [DllImport("..\\LIB\\PInvokeLib.dll")]
    internal static extern void TestUnion(MyUnion u, int type);

    [DllImport("..\\LIB\\PInvokeLib.dll")]
    internal static extern void TestUnion2(MyUnion2_1 u, int type);

    [DllImport("..\\LIB\\PInvokeLib.dll")]
    internal static extern void TestUnion2(MyUnion2_2 u, int type);
}
' Declares managed structures instead of unions.
<StructLayout(LayoutKind.Explicit)>
Public Structure MyUnion
    <FieldOffset(0)> Public i As Integer
    <FieldOffset(0)> Public d As Double
End Structure

<StructLayout(LayoutKind.Explicit, Size:=128)>
Public Structure MyUnion2_1
    <FieldOffset(0)> Public i As Integer
End Structure

<StructLayout(LayoutKind.Sequential)>
Public Structure MyUnion2_2
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=128)>
    Public str As String
End Structure

Friend Class NativeMethods
    ' Declares managed prototypes for unmanaged function.
    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Sub TestUnion(
        ByVal u As MyUnion, ByVal type As Integer)
    End Sub

    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Overloads Shared Sub TestUnion2(
        ByVal u As MyUnion2_1, ByVal type As Integer)
    End Sub

    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Overloads Shared Sub TestUnion2(
        ByVal u As MyUnion2_2, ByVal type As Integer)
    End Sub
End Class

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

public ref class App
{
public:
    static void Main()
    {
        MyUnion mu;// = new MyUnion();
        mu.i = 99;
        NativeMethods::TestUnion(mu, 1);

        mu.d = 99.99;
        NativeMethods::TestUnion(mu, 2);

        MyUnion2_1 mu2_1;// = new MyUnion2_1();
        mu2_1.i = 99;
        NativeMethods::TestUnion2(mu2_1, 1);

        MyUnion2_2 mu2_2;// = new MyUnion2_2();
        mu2_2.str = "*** string ***";
        NativeMethods::TestUnion2(mu2_2, 2);
    }
};
public class App
{
    public static void Main()
    {
        MyUnion mu = new MyUnion();
        mu.i = 99;
        NativeMethods.TestUnion(mu, 1);

        mu.d = 99.99;
        NativeMethods.TestUnion(mu, 2);

        MyUnion2_1 mu2_1 = new MyUnion2_1();
        mu2_1.i = 99;
        NativeMethods.TestUnion2(mu2_1, 1);

        MyUnion2_2 mu2_2 = new MyUnion2_2();
        mu2_2.str = "*** string ***";
        NativeMethods.TestUnion2(mu2_2, 2);
    }
}
Public Class App
    Public Shared Sub Main()
        Dim mu As New MyUnion()
        mu.i = 99
        NativeMethods.TestUnion(mu, 1)

        mu.d = 99.99
        NativeMethods.TestUnion(mu, 2)

        Dim mu2_1 As New MyUnion2_1()
        mu2_1.i = 99
        NativeMethods.TestUnion2(mu2_1, 1)

        Dim mu2_2 As New MyUnion2_2()
        mu2_2.str = "*** string ***"
        NativeMethods.TestUnion2(mu2_2, 2)
    End Sub
End Class

Пример SysTimeSysTime sample

В этом примере показано, как передать указатель на класс в неуправляемую функцию, ожидающую указатель на структуру.This sample demonstrates how to pass a pointer to a class to an unmanaged function that expects a pointer to a structure.

В примере используется следующая неуправляемая функция, показанная с исходным объявлением:The SysTime sample uses the following unmanaged function, shown with its original function declaration:

  • функция GetSystemTime, экспортированная из Kernel32.dll.GetSystemTime exported from Kernel32.dll.

    VOID GetSystemTime(LPSYSTEMTIME lpSystemTime);
    

Исходная структура, переданная в функцию, содержит следующие элементы:The original structure passed to the function contains the following elements:

typedef struct _SYSTEMTIME {
    WORD wYear;
    WORD wMonth;
    WORD wDayOfWeek;
    WORD wDay;
    WORD wHour;
    WORD wMinute;
    WORD wSecond;
    WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;

В этом примере класс SystemTime содержит элементы исходной структуры, представленные в виде членов класса.In this sample, the SystemTime class contains the elements of the original structure represented as class members. Чтобы гарантировать последовательное размещение членов в памяти в порядке их появления, применяется атрибут StructLayoutAttribute .The StructLayoutAttribute attribute is set to ensure that the members are arranged in memory sequentially, in the order in which they appear.

Класс NativeMethods содержит управляемый прототип метода GetSystemTime, который по умолчанию передает класс SystemTime в качестве параметра In или Out.The NativeMethods class contains a managed prototype of the GetSystemTime method, which passes the SystemTime class as an In/Out parameter by default. Этот параметр должен быть объявлен с атрибутами InAttribute и OutAttribute, так как классы, являющиеся ссылочными типами, по умолчанию передаются как параметры In.The parameter must be declared with the InAttribute and OutAttribute attributes because classes, which are reference types, are passed as In parameters by default. Чтобы вызывающий объект получал результаты, необходимо явным образом применить атрибуты направления.For the caller to receive the results, these directional attributes must be applied explicitly. Класс App создает экземпляр класса SystemTime и осуществляет доступ к его полям данных.The App class creates a new instance of the SystemTime class and accesses its data fields.

Примеры кодаCode Samples

using namespace System;
using namespace System::Runtime::InteropServices;     // For StructLayout, DllImport

[StructLayout(LayoutKind::Sequential)]
public ref class SystemTime
{
public:
    unsigned short year;
    unsigned short month;
    unsigned short weekday;
    unsigned short day;
    unsigned short hour;
    unsigned short minute;
    unsigned short second;
    unsigned short millisecond;
};

public class NativeMethods
{
public:
    // Declares a managed prototype for the unmanaged function using Platform Invoke.
    [DllImport("Kernel32.dll")]
    static void GetSystemTime([In, Out] SystemTime^ st);
};

public class App
{
public:
    static void Main()
    {
        Console::WriteLine("C++/CLI SysTime Sample using Platform Invoke");
        SystemTime^ st = gcnew SystemTime();
        NativeMethods::GetSystemTime(st);
        Console::Write("The Date is: ");
        Console::Write("{0} {1} {2}", st->month, st->day, st->year);
    }
};

int main()
{
    App::Main();
}
// The program produces output similar to the following:
//
// C++/CLI SysTime Sample using Platform Invoke
// The Date is: 3 21 2010
using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public class SystemTime
{
    public ushort year;
    public ushort month;
    public ushort weekday;
    public ushort day;
    public ushort hour;
    public ushort minute;
    public ushort second;
    public ushort millisecond;
}

internal static class NativeMethods
{
    // Declares a managed prototype for the unmanaged function using Platform Invoke.
    [DllImport("Kernel32.dll")]
    internal static extern void GetSystemTime([In, Out] SystemTime st);
}

public class App
{
    public static void Main()
    {
        Console.WriteLine("C# SysTime Sample using Platform Invoke");
        SystemTime st = new SystemTime();
        NativeMethods.GetSystemTime(st);
        Console.Write("The Date is: ");
        Console.Write($"{st.month} {st.day} {st.year}");
    }
}

// The program produces output similar to the following:
//
// C# SysTime Sample using Platform Invoke
// The Date is: 3 21 2010
Imports System.Runtime.InteropServices

' Declares a class member for each structure element.
<StructLayout(LayoutKind.Sequential)>
Public Class SystemTime
    Public year As Short
    Public month As Short
    Public weekday As Short
    Public day As Short
    Public hour As Short
    Public minute As Short
    Public second As Short
    Public millisecond As Short
End Class

Friend Class NativeMethods
    ' Declares a managed prototype for the unmanaged function.
    Friend Declare Sub GetSystemTime Lib "Kernel32.dll" (
        <[In](), Out()> ByVal st As SystemTime)
End Class

Public Class App
    Public Shared Sub Main()
        Console.WriteLine("VB .NET SysTime Sample using Platform Invoke")
        Dim st As New SystemTime()
        NativeMethods.GetSystemTime(st)
        Console.Write($"The Date is: {st.month} {st.day} {st.year}")
    End Sub
End Class
' The program produces output similar to the following:
'
' VB .NET SysTime Sample using Platform Invoke
' The Date is: 3 21 2010

Пример OutArrayOfStructsOutArrayOfStructs sample

В этом примере показано, как передать в неуправляемую функцию массив структур, содержащий целочисленные значения и строки, в виде параметров Out.This sample shows how to pass an array of structures that contains integers and strings as Out parameters to an unmanaged function.

В этом примере демонстрируется, как вызывать собственную функцию с помощью класса Marshal и небезопасного кода.This sample demonstrates how to call a native function by using the Marshal class and by using unsafe code.

В примере используются функции-оболочки и вызовы неуправляемого кода, определенные в библиотеке PinvokeLib.dll и содержащиеся в исходных файлах.This sample uses a wrapper functions and platform invokes defined in PinvokeLib.dll, also provided in the source files. В нем используется функция TestOutArrayOfStructs и структура MYSTRSTRUCT2.It uses the TestOutArrayOfStructs function and the MYSTRSTRUCT2 structure. Структура содержит следующие элементы:The structure contains the following elements:

typedef struct _MYSTRSTRUCT2
{
   char* buffer;
   UINT size;
} MYSTRSTRUCT2;

Класс MyStruct содержит строковый объект из символов ANSI.The MyStruct class contains a string object of ANSI characters. Поле CharSet определяет формат ANSI.The CharSet field specifies ANSI format. MyUnsafeStruct — это структура, содержащая тип IntPtr вместо строки.MyUnsafeStruct, is a structure containing an IntPtr type instead of a string.

Класс NativeMethods содержит перегруженный метод прототипа TestOutArrayOfStructs.The NativeMethods class contains the overloaded TestOutArrayOfStructs prototype method. Если в качестве параметра метод объявляет указатель, класс должен быть помечен зарезервированным словом unsafe.If a method declares a pointer as a parameter, the class should be marked with the unsafe keyword. Так как Visual Basic не может использовать небезопасный код, перегруженный метод, модификатор unsafe и структуры MyUnsafeStruct не нужны.Because Visual Basic cannot use unsafe code, the overloaded method, unsafe modifier, and the MyUnsafeStruct structure are unnecessary.

Класс App реализует метод UsingMarshaling, который выполняет все задачи, необходимые для передачи массива.The App class implements the UsingMarshaling method, which performs all the tasks necessary to pass the array. Чтобы указать, что данные передаются от вызываемого объекта к вызывающему, массив помечается зарезервированным словом out (ByRef в Visual Basic).The array is marked with the out (ByRef in Visual Basic) keyword to indicate that data passes from callee to caller. Реализация использует следующие методы класса Marshal:The implementation uses the following Marshal class methods:

  • метод PtrToStructure для маршалинга данных из неуправляемого буфера в управляемый объект;PtrToStructure to marshal data from the unmanaged buffer to a managed object.

  • метод DestroyStructure для освобождения памяти, зарезервированной для строк структуры;DestroyStructure to release the memory reserved for strings in the structure.

  • метод FreeCoTaskMem для освобождения памяти, зарезервированной для массива.FreeCoTaskMem to release the memory reserved for the array.

Как упоминалось ранее, C# допускает небезопасный код, который Visual Basic не поддерживает.As previously mentioned, C# allows unsafe code and Visual Basic does not. В примере кода C# UsingUnsafePointer является альтернативной реализацией метода, в которой для обратной передачи массива, содержащего структуру MyUnsafeStruct, вместо класса Marshal используются указатели.In the C# sample, UsingUnsafePointer is an alternative method implementation that uses pointers instead of the Marshal class to pass back the array containing the MyUnsafeStruct structure.

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

// Declares a class member for each structure element.
[StructLayout(LayoutKind::Sequential, CharSet = CharSet::Ansi)]
public ref class MyStruct
{
public:
    String^ buffer;
    int size;
};

// Declares a structure with a pointer.
[StructLayout(LayoutKind::Sequential)]
public value struct MyUnsafeStruct
{
public:
    IntPtr buffer;
    int size;
};

private ref class NativeMethods
{
public:
    // Declares managed prototypes for the unmanaged function.
    [DllImport("..\\LIB\\PInvokeLib.dll")]
    static void TestOutArrayOfStructs(int% size, IntPtr% outArray);

    [DllImport("..\\LIB\\PInvokeLib.dll")]
    static void TestOutArrayOfStructs(int% size, MyUnsafeStruct** outArray);
};
// Declares a class member for each structure element.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class MyStruct
{
    public string buffer;
    public int size;
}

// Declares a structure with a pointer.
[StructLayout(LayoutKind.Sequential)]
public struct MyUnsafeStruct
{
    public IntPtr buffer;
    public int size;
}

internal static unsafe class NativeMethods
{
    // Declares managed prototypes for the unmanaged function.
    [DllImport("..\\LIB\\PInvokeLib.dll")]
    internal static extern void TestOutArrayOfStructs(
        out int size, out IntPtr outArray);

    [DllImport("..\\LIB\\PInvokeLib.dll")]
    internal static extern void TestOutArrayOfStructs(
        out int size, MyUnsafeStruct** outArray);
}
' Declares a class member for each structure element.
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Ansi)>
Public Class MyStruct
    Public buffer As String
    Public someSize As Integer
End Class

Friend Class NativeMethods
    ' Declares a managed prototype for the unmanaged function.
    <DllImport("..\LIB\PinvokeLib.dll", CallingConvention:=CallingConvention.Cdecl)>
    Friend Shared Sub TestOutArrayOfStructs(
        ByRef arrSize As Integer, ByRef outArray As IntPtr)
    End Sub
End Class

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

public ref class App
{
public:
    static void Main()
    {
        Console::WriteLine("\nUsing marshal class\n");
        UsingMarshaling();
        Console::WriteLine("\nUsing unsafe code\n");
        UsingUnsafePointer();
    }

    static void UsingMarshaling()
    {
        int size;
        IntPtr outArray;

        NativeMethods::TestOutArrayOfStructs(size, outArray);
        array<MyStruct^>^ manArray = gcnew array<MyStruct^>(size);
        IntPtr current = outArray;
        for (int i = 0; i < size; i++)
        {
            manArray[i] = gcnew MyStruct();
            Marshal::PtrToStructure(current, manArray[i]);

            Marshal::DestroyStructure(current, MyStruct::typeid);
            //current = (IntPtr)((long)current + Marshal::SizeOf(manArray[i]));
            current = current + Marshal::SizeOf(manArray[i]);

            Console::WriteLine("Element {0}: {1} {2}", i, manArray[i]->buffer,
                manArray[i]->size);
        }
        Marshal::FreeCoTaskMem(outArray);
    }

    static void UsingUnsafePointer()
    {
        int size;
        MyUnsafeStruct* pResult;

        NativeMethods::TestOutArrayOfStructs(size, &pResult);
        MyUnsafeStruct* pCurrent = pResult;
        for (int i = 0; i < size; i++, pCurrent++)
        {
            Console::WriteLine("Element {0}: {1} {2}", i,
                Marshal::PtrToStringAnsi(pCurrent->buffer), pCurrent->size);
            Marshal::FreeCoTaskMem(pCurrent->buffer);
        }
        Marshal::FreeCoTaskMem((IntPtr)pResult);
    }
};
public class App
{
    public static void Main()
    {
        Console.WriteLine("\nUsing marshal class\n");
        UsingMarshaling();
        Console.WriteLine("\nUsing unsafe code\n");
        UsingUnsafePointer();
    }

    public static void UsingMarshaling()
    {
        int size;
        IntPtr outArray;

        NativeMethods.TestOutArrayOfStructs(out size, out outArray);
        MyStruct[] manArray = new MyStruct[size];
        IntPtr current = outArray;
        for (int i = 0; i < size; i++)
        {
            manArray[i] = new MyStruct();
            Marshal.PtrToStructure(current, manArray[i]);

            //Marshal.FreeCoTaskMem((IntPtr)Marshal.ReadInt32(current));
            Marshal.DestroyStructure(current, typeof(MyStruct));
            current = (IntPtr)((long)current + Marshal.SizeOf(manArray[i]));

            Console.WriteLine("Element {0}: {1} {2}", i, manArray[i].buffer,
                manArray[i].size);
        }

        Marshal.FreeCoTaskMem(outArray);
    }

    public static unsafe void UsingUnsafePointer()
    {
        int size;
        MyUnsafeStruct* pResult;

        NativeMethods.TestOutArrayOfStructs(out size, &pResult);
        MyUnsafeStruct* pCurrent = pResult;
        for (int i = 0; i < size; i++, pCurrent++)
        {
            Console.WriteLine("Element {0}: {1} {2}", i,
                Marshal.PtrToStringAnsi(pCurrent->buffer), pCurrent->size);
            Marshal.FreeCoTaskMem(pCurrent->buffer);
        }

        Marshal.FreeCoTaskMem((IntPtr)pResult);
    }
}
Public Class App
    Public Shared Sub Main()
        Console.WriteLine(vbNewLine + "Using marshal class" + vbNewLine)
        UsingMarshaling()
        'Visual Basic 2005 cannot use unsafe code.
    End Sub

    Public Shared Sub UsingMarshaling()
        Dim arrSize As Integer
        Dim outArray As IntPtr

        NativeMethods.TestOutArrayOfStructs(arrSize, outArray)
        Dim manArray(arrSize - 1) As MyStruct
        Dim current As IntPtr = outArray
        Dim i As Integer

        For i = 0 To arrSize - 1
            manArray(i) = New MyStruct()
            Marshal.PtrToStructure(current, manArray(i))

            Marshal.DestroyStructure(current, GetType(MyStruct))
            current = IntPtr.op_Explicit(current.ToInt64() _
                + Marshal.SizeOf(manArray(i)))

            Console.WriteLine("Element {0}: {1} {2}", i, manArray(i).
                buffer, manArray(i).someSize)
        Next i
        Marshal.FreeCoTaskMem(outArray)
    End Sub
End Class

См. также:See also