Delen via


Marshallklassen, structuren en vakbonden

Klassen en structuren zijn vergelijkbaar in .NET Framework. Beide kunnen velden, eigenschappen en gebeurtenissen hebben. Ze kunnen ook statische en niet-statische methoden hebben. Een belangrijk verschil is dat structuren waardetypen zijn en klassen verwijzingstypen zijn.

De volgende tabel bevat marshallopties voor klassen, structuren en vakbonden; beschrijft hun gebruik; en biedt een koppeling naar het bijbehorende voorbeeld van het aanroepen van het platform.

Type Description Voorbeeld
Klasse op waarde. Geeft een klasse door met gehele getallen als een In/Out-parameter, zoals de beheerde case. SysTime-voorbeeld
Structuur op waarde. Geeft structuren door als In-parameters. Voorbeeld van structuren
Structuur per verwijzing. Geeft structuren door als In/Out-parameters. OSInfo-voorbeeld
Structuur met geneste structuren (afgevlakt). Geeft een klasse door die een structuur vertegenwoordigt met geneste structuren in de onbeheerde functie. De structuur wordt afgevlakt tot één grote structuur in het beheerde prototype. FindFile-voorbeeld
Structuur met een aanwijzer naar een andere structuur. Hiermee wordt een structuur met een aanwijzer als lid doorgegeven aan een tweede structuur. Voorbeeld van structuren
Matrix van structuren met gehele getallen per waarde. Hiermee wordt een matrix van structuren doorgegeven die alleen gehele getallen bevatten als een in/uit-parameter. Leden van de matrix kunnen worden gewijzigd. Voorbeeld van matrices
Matrix van structuren met gehele getallen en tekenreeksen per verwijzing. Geeft een matrix van structuren door die gehele getallen en tekenreeksen als een Out-parameter bevatten. Met de aangeroepen functie wordt geheugen toegewezen voor de matrix. OutArrayOfStructs-voorbeeld
Samenvoegingen met waardetypen. Hiermee worden samenvoegingen doorgegeven met waardetypen (geheel getal en dubbel). Voorbeeld van samenvoegingen
Samenvoegingen met gemengde typen. Geeft samenvoegingen door met gemengde typen (geheel getal en tekenreeks). Voorbeeld van samenvoegingen
Struct met platformspecifieke indeling. Geeft een type door met systeemeigen verpakkingsdefinities. Platformvoorbeeld
Null-waarden in structuur. Geeft een null-verwijzing (niets in Visual Basic) door in plaats van een verwijzing naar een waardetype. HandleRef-voorbeeld

Voorbeeld van structuren

In dit voorbeeld ziet u hoe u een structuur doorgeeft die verwijst naar een tweede structuur, een structuur doorgeeft met een ingesloten structuur en een structuur doorgeeft met een ingesloten matrix.

In het voorbeeld Structs worden de volgende onbeheerde functies gebruikt, weergegeven met de oorspronkelijke functiedeclaratie:

  • TestStructInStruct geëxporteerd uit PinvokeLib.dll.

    int TestStructInStruct(MYPERSON2* pPerson2);
    
  • TestStructInStruct3 geëxporteerd uit PinvokeLib.dll.

    void TestStructInStruct3(MYPERSON3 person3);
    
  • TestArrayInStruct geëxporteerd uit PinvokeLib.dll.

    void TestArrayInStruct(MYARRAYSTRUCT* pStruct);
    

PinvokeLib.dll is een aangepaste niet-beheerde bibliotheek met implementaties voor de eerder vermelde functies en vier structuren: MYPERSON, MYPERSON2, MYPERSON3 en MYARRAYSTRUCT. Deze structuren bevatten de volgende elementen:

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;

De beheerdeMyPerson, MyPerson2, en MyPerson3MyArrayStruct structuren hebben het volgende kenmerk:

  • MyPerson bevat alleen tekenreeksleden. Het veld CharSet stelt de tekenreeksen in op ANSI-indeling wanneer deze wordt doorgegeven aan de niet-beheerde functie.

  • MyPerson2 bevat een IntPtr aan de MyPerson structuur. Het type IntPtr vervangt de oorspronkelijke aanwijzer naar de onbeheerde structuur, omdat .NET Framework-toepassingen geen aanwijzers gebruiken tenzij de code als onveilig is gemarkeerd.

  • MyPerson3 bevat MyPerson als een ingesloten structuur. Een structuur die in een andere structuur is ingesloten, kan worden afgevlakt door de elementen van de ingesloten structuur rechtstreeks in de hoofdstructuur te plaatsen, of als een ingesloten structuur, zoals in dit voorbeeld wordt gedaan.

  • MyArrayStruct bevat een matrix met gehele getallen. Het MarshalAsAttribute kenmerk stelt de UnmanagedType opsommingswaarde in op ByValArray, die wordt gebruikt om het aantal elementen in de matrix aan te geven.

Voor alle structuren in dit voorbeeld wordt het StructLayoutAttribute kenmerk toegepast om ervoor te zorgen dat de leden opeenvolgend in het geheugen zijn gerangschikt, in de volgorde waarin ze worden weergegeven.

De NativeMethods klasse bevat beheerde prototypen voor de TestStructInStruct, TestStructInStruct3en TestArrayInStruct methoden die door de App klasse worden aangeroepen. Elk prototype declareert als volgt één parameter:

  • TestStructInStruct declareert een verwijzing naar het type MyPerson2 als parameter.

  • TestStructInStruct3 declareert het type MyPerson3 als de parameter en geeft de parameter door op waarde.

  • TestArrayInStruct declareert een verwijzing naar het type MyArrayStruct als parameter.

Structuren als argumenten voor methoden worden doorgegeven door waarde, tenzij de parameter het trefwoord ref (ByRef in Visual Basic) bevat. De methode geeft bijvoorbeeld TestStructInStruct een verwijzing (de waarde van een adres) door aan een object van het type MyPerson2 aan onbeheerde code. Als u de structuur wilt bewerken waarnaar MyPerson2 wordt verwezen, maakt het voorbeeld een buffer van een opgegeven grootte en retourneert het bijbehorende adres door de Marshal.AllocCoTaskMem en Marshal.SizeOf methoden te combineren. Vervolgens kopieert het voorbeeld de inhoud van de beheerde structuur naar de onbeheerde buffer. Ten slotte gebruikt het voorbeeld de Marshal.PtrToStructure methode om marshal data van de onbeheerde buffer naar een beheerd object en de Marshal.FreeCoTaskMem methode om het onbeheerde geheugenblok vrij te maken.

Prototypen declareren

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

Aanroepende functies

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

FindFile-voorbeeld

In dit voorbeeld ziet u hoe u een structuur met een tweede, ingesloten structuur doorgeeft aan een onbeheerde functie. Het laat ook zien hoe u het MarshalAsAttribute kenmerk gebruikt om een matrix met vaste lengte binnen de structuur te declareren. In dit voorbeeld worden de ingesloten structuurelementen toegevoegd aan de bovenliggende structuur. Zie Het voorbeeld van structuren voor een voorbeeld van een ingesloten structuur die niet is afgevlakt.

Het FindFile-voorbeeld maakt gebruik van de volgende onbeheerde functie, weergegeven met de oorspronkelijke functiedeclaratie:

  • FindFirstFile geëxporteerd uit Kernel32.dll.

    HANDLE FindFirstFile(LPCTSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData);
    

De oorspronkelijke structuur die aan de functie wordt doorgegeven, bevat de volgende elementen:

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;

In dit voorbeeld bevat de FindData klasse een corresponderend gegevenslid voor elk element van de oorspronkelijke structuur en de ingesloten structuur. In plaats van twee oorspronkelijke tekenbuffers vervangt de klasse tekenreeksen. MarshalAsAttribute stelt de UnmanagedType opsomming in op ByValTStr, die wordt gebruikt om de inline tekenmatrices met een vaste lengte te identificeren die in de niet-beheerde structuren worden weergegeven.

De NativeMethods klasse bevat een beheerd prototype van de FindFirstFile methode, die de FindData klasse doorgeeft als een parameter. De parameter moet worden gedeclareerd met de InAttribute en OutAttribute kenmerken omdat klassen, die verwijzingstypen zijn, standaard worden doorgegeven als In-parameters.

Prototypen declareren

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

Aanroepende functies

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

Voorbeeld van samenvoegingen

In dit voorbeeld ziet u hoe u structuren met alleen waardetypen en structuren met een waardetype en een tekenreeks als parameters doorgeeft aan een onbeheerde functie die een samenvoeging verwacht. Een samenvoeging vertegenwoordigt een geheugenlocatie die kan worden gedeeld door twee of meer variabelen.

In het voorbeeld unions wordt de volgende onbeheerde functie gebruikt, die wordt weergegeven met de oorspronkelijke functiedeclaratie:

  • TestUnion geëxporteerd uit PinvokeLib.dll.

    void TestUnion(MYUNION u, int type);
    

PinvokeLib.dll is een aangepaste niet-beheerde bibliotheek die een implementatie bevat voor de eerder vermelde functie en twee samenvoegingen, MYUNION en MYUNION2. De samenvoegingen bevatten de volgende elementen:

union MYUNION
{
    int number;
    double d;
}

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

In beheerde code worden samenvoegingen gedefinieerd als structuren. De MyUnion structuur bevat twee waardetypen als leden: een geheel getal en een dubbele waarde. Het StructLayoutAttribute kenmerk is ingesteld om de exacte positie van elk gegevenslid te bepalen. Het FieldOffsetAttribute kenmerk biedt de fysieke positie van velden binnen de onbeheerde weergave van een samenvoeging. U ziet dat beide leden dezelfde offsetwaarden hebben, zodat de leden hetzelfde geheugen kunnen definiëren.

MyUnion2_1 en MyUnion2_2 een waardetype (geheel getal) en een tekenreeks bevatten. In beheerde code mogen waardetypen en verwijzingstypen elkaar niet overlappen. In dit voorbeeld wordt overbelasting van methoden gebruikt om de aanroeper in staat te stellen beide typen te gebruiken bij het aanroepen van dezelfde onbeheerde functie. De indeling MyUnion2_1 is expliciet en heeft een exacte offsetwaarde. Heeft daarentegen MyUnion2_2 een sequentiële indeling, omdat expliciete indelingen niet zijn toegestaan met verwijzingstypen. Het MarshalAsAttribute kenmerk stelt de UnmanagedType opsomming in op ByValTStr, die wordt gebruikt om de inline tekenmatrices met een vaste lengte te identificeren die worden weergegeven in de niet-beheerde weergave van de samenvoeging.

De NativeMethods klasse bevat de prototypen voor de TestUnion en TestUnion2 methoden. TestUnion2 is overbelast om te declareren MyUnion2_1 of MyUnion2_2 als parameters.

Prototypen declareren

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

Aanroepende functies

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

Platformvoorbeeld

In sommige scenario's struct kunnen indelingen union verschillen, afhankelijk van het beoogde platform. Denk bijvoorbeeld aan het STRRET type wanneer dit is gedefinieerd in een COM-scenario:

#include <pshpack8.h> /* Defines the packing of the struct */
typedef struct _STRRET
    {
    UINT uType;
    /* [switch_is][switch_type] */ union
        {
        /* [case()][string] */ LPWSTR pOleStr;
        /* [case()] */ UINT uOffset;
        /* [case()] */ char cStr[ 260 ];
        }  DUMMYUNIONNAME;
    }  STRRET;
#include <poppack.h>

Het bovenstaande struct wordt gedeclareerd met Windows-headers die van invloed zijn op de geheugenindeling van het type. Wanneer deze indelingsgegevens zijn gedefinieerd in een beheerde omgeving, zijn deze indelingsgegevens nodig om correct te kunnen samenwerken met systeemeigen code.

De juiste beheerde definitie van dit type in een 32-bits proces is:

[StructLayout(LayoutKind.Explicit, Size = 264)]
public struct STRRET_32
{
    [FieldOffset(0)]
    public uint uType;

    [FieldOffset(4)]
    public IntPtr pOleStr;

    [FieldOffset(4)]
    public uint uOffset;

    [FieldOffset(4)]
    public IntPtr cStr;
}

Bij een 64-bits proces zijn de grootte - en veldverschuivingen anders. De juiste indeling is:

[StructLayout(LayoutKind.Explicit, Size = 272)]
public struct STRRET_64
{
    [FieldOffset(0)]
    public uint uType;

    [FieldOffset(8)]
    public IntPtr pOleStr;

    [FieldOffset(8)]
    public uint uOffset;

    [FieldOffset(8)]
    public IntPtr cStr;
}

Als u niet goed rekening houdt met de systeemeigen indeling in een interoperabiliteitsscenario, kan dit leiden tot willekeurige crashes of slechtere, onjuiste berekeningen.

.NET-assembly's kunnen standaard worden uitgevoerd in zowel een 32-bits als een 64-bits versie van de .NET-runtime. De app moet wachten tot de uitvoeringstijd om te bepalen welke van de vorige definities moet worden gebruikt.

In het volgende codefragment ziet u een voorbeeld van het kiezen tussen de 32-bits en 64-bits definitie tijdens runtime.

if (IntPtr.Size == 8)
{
    // Use the STRRET_64 definition
}
else
{
    Debug.Assert(IntPtr.Size == 4);
    // Use the STRRET_32 definition
}

SysTime-voorbeeld

In dit voorbeeld ziet u hoe u een aanwijzer doorgeeft aan een klasse aan een onbeheerde functie die een aanwijzer naar een structuur verwacht.

In het SysTime-voorbeeld wordt de volgende onbeheerde functie gebruikt, die wordt weergegeven met de oorspronkelijke functiedeclaratie:

  • GetSystemTime geëxporteerd uit Kernel32.dll.

    VOID GetSystemTime(LPSYSTEMTIME lpSystemTime);
    

De oorspronkelijke structuur die aan de functie wordt doorgegeven, bevat de volgende elementen:

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

In dit voorbeeld bevat de SystemTime klasse de elementen van de oorspronkelijke structuur die wordt weergegeven als klasseleden. Het StructLayoutAttribute kenmerk wordt ingesteld om ervoor te zorgen dat de leden opeenvolgend in het geheugen zijn gerangschikt, in de volgorde waarin ze worden weergegeven.

De NativeMethods klasse bevat een beheerd prototype van de GetSystemTime methode, die de SystemTime klasse standaard doorgeeft als een In/Out-parameter. De parameter moet worden gedeclareerd met de InAttribute en OutAttribute kenmerken omdat klassen, die verwijzingstypen zijn, standaard worden doorgegeven als In-parameters. Voor het ontvangen van de resultaten moeten deze directionele kenmerken expliciet worden toegepast. De App klasse maakt een nieuw exemplaar van de SystemTime klasse en opent de bijbehorende gegevensvelden.

Codevoorbeelden

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

OutArrayOfStructs-voorbeeld

In dit voorbeeld ziet u hoe u een matrix met structuren met gehele getallen en tekenreeksen als Out-parameters doorgeeft aan een onbeheerde functie.

In dit voorbeeld ziet u hoe u een systeemeigen functie aanroept met behulp van de Marshal klasse en met behulp van onveilige code.

In dit voorbeeld wordt gebruikgemaakt van wrapper-functies en platformen die zijn gedefinieerd in PinvokeLib.dll, ook beschikbaar in de bronbestanden. De functie en de MYSTRSTRUCT2 structuur worden gebruiktTestOutArrayOfStructs. De structuur bevat de volgende elementen:

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

De MyStruct klasse bevat een tekenreeksobject van ANSI-tekens. In het CharSet veld wordt de ANSI-indeling opgegeven. MyUnsafeStruct, is een structuur met een IntPtr type in plaats van een tekenreeks.

De NativeMethods klasse bevat de overbelaste TestOutArrayOfStructs prototypemethode. Als een methode een aanwijzer als parameter declareert, moet de klasse worden gemarkeerd met het unsafe trefwoord. Omdat Visual Basic geen onveilige code kan gebruiken, zijn de overbelaste methode, onveilige modifier en de MyUnsafeStruct structuur overbodig.

De App klasse implementeert de UsingMarshaling methode, waarmee alle taken worden uitgevoerd die nodig zijn om de matrix door te geven. De matrix wordt gemarkeerd met het out trefwoord (ByRef in Visual Basic) om aan te geven dat gegevens worden doorgegeven van de aanroeper naar de aanroeper. De implementatie maakt gebruik van de volgende Marshal klassemethoden:

  • PtrToStructure marshal data from the unmanaged buffer to a managed object.

  • DestroyStructure om het geheugen vrij te geven dat is gereserveerd voor tekenreeksen in de structuur.

  • FreeCoTaskMem om het geheugen vrij te geven dat is gereserveerd voor de matrix.

Zoals eerder vermeld, staat C# onveilige code toe en Visual Basic niet. In het C#-voorbeeld UsingUnsafePointer is een alternatieve methode-implementatie die pointers gebruikt in plaats van de Marshal klasse om de matrix met de MyUnsafeStruct structuur door te geven.

Prototypen declareren

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

Aanroepende functies

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

Zie ook