Sdílet prostřednictvím


Seřazování tříd, struktur a sjednocení

Třídy a struktury jsou podobné v rozhraní .NET Framework. Obě můžou obsahovat pole, vlastnosti a události. Můžou mít také statické a nestatické metody. Jedním z hlavních rozdílů je, že struktury jsou typy hodnot a třídy jsou odkazové typy.

Následující tabulka uvádí možnosti zařazování tříd, struktur a sjednocení; popisuje jejich využití; a poskytuje odkaz na odpovídající ukázku vyvolání platformy.

Typ Popis Vzorek
Třída podle hodnoty. Předá třídu s celočíselnou členy jako parametr In/Out, jako je spravovaný případ. Ukázka SysTime
Strukturovat podle hodnoty. Předává struktury jako parametry In. Ukázka struktur
Struktura podle odkazu. Předává struktury jako parametry In/Out. Ukázka OSInfo
Struktura s vnořenými strukturami (zploštěná). Předá třídu, která představuje strukturu s vnořenými strukturami v nespravované funkci. Struktura je zploštěna na jednu velkou strukturu ve spravovaném prototypu. Ukázka FindFile
Struktura s ukazatelem na jinou strukturu Předá strukturu, která obsahuje ukazatel na druhou strukturu jako člen. Ukázka struktur
Pole struktur s celými čísly podle hodnoty Předá pole struktur, které obsahují pouze celá čísla jako parametr In/Out. Členy pole lze změnit. Ukázka polí
Pole struktur s celými čísly a řetězci podle odkazu. Předává pole struktur, které obsahují celá čísla a řetězce jako parametr Out. Volaná funkce přiděluje paměť pro pole. OutArrayOfStructs – ukázka
Sjednocení s typy hodnot. Předává sjednocení s typy hodnot (celé číslo a double). Ukázka sjednocení
Sjednocení se smíšenými typy. Předává sjednocení se smíšenými typy (celé číslo a řetězec). Ukázka sjednocení
Struktura s rozložením specifickou pro platformu Předává typ s definicemi nativního balení. Ukázka platformy
Hodnoty null ve struktuře. Předá odkaz null (Nothing v jazyce Visual Basic) místo odkazu na typ hodnoty. Ukázka HandleRef

Ukázka struktur

Tato ukázka ukazuje, jak předat strukturu, která odkazuje na druhou strukturu, předat strukturu s vloženou strukturou a předat strukturu s vloženým polem.

Ukázka struktur používá následující nespravované funkce zobrazené s jejich původní deklarací funkce:

  • TestStructInStruct exportovaný z PinvokeLib.dll.

    int TestStructInStruct(MYPERSON2* pPerson2);
    
  • TestStructInStruct3 exportovaný z PinvokeLib.dll

    void TestStructInStruct3(MYPERSON3 person3);
    
  • TestArrayInStruct exportovaný z PinvokeLib.dll

    void TestArrayInStruct(MYARRAYSTRUCT* pStruct);
    

PinvokeLib.dll je vlastní nespravovaná knihovna, která obsahuje implementace pro dříve uvedené funkce a čtyři struktury: MYPERSON, MYPERSON2, MYPERSON3 a MYARRAYSTRUCT. Tyto struktury obsahují následující prvky:

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;

Spravované MyPerson, MyPerson2, MyPerson3a MyArrayStruct struktury mají následující charakteristické vlastnosti:

  • MyPerson obsahuje pouze členy řetězců. Pole CharSet nastaví řetězce na formát ANSI při předání do nespravované funkce.

  • MyPerson2obsahuje IntPtr do MyPerson struktury. Typ IntPtr nahrazuje původní ukazatel na nespravovanou strukturu, protože aplikace rozhraní .NET Framework nepoužívají ukazatele, pokud kód není označen jako nebezpečný.

  • MyPerson3 obsahuje MyPerson jako vloženou strukturu. Strukturu vloženou do jiné struktury lze zploštět umístěním prvků vložené struktury přímo do hlavní struktury nebo může být ponechána jako vložená struktura, stejně jako v tomto vzorku.

  • MyArrayStruct obsahuje pole celých čísel. Atribut MarshalAsAttribute nastaví hodnotu výčtu UnmanagedType na ByValArray, který se používá k označení počtu prvků v poli.

U všech struktur v této ukázce se atribut použije, aby se zajistilo, StructLayoutAttribute že jsou členy uspořádány v paměti postupně v pořadí, v jakém se zobrazují.

Třída NativeMethods obsahuje spravované prototypy pro TestStructInStruct, TestStructInStruct3a TestArrayInStruct metody volané App třídou. Každý prototyp deklaruje jeden parametr následujícím způsobem:

  • TestStructInStruct deklaruje odkaz na typ MyPerson2 jako jeho parametr.

  • TestStructInStruct3 deklaruje typ MyPerson3 jako jeho parametr a předá parametr podle hodnoty.

  • TestArrayInStruct deklaruje odkaz na typ MyArrayStruct jako jeho parametr.

Struktury jako argumenty metodám jsou předány hodnotou, pokud parametr neobsahuje klíčové slovo ref (ByRef v jazyce Visual Basic). Například TestStructInStruct metoda předává odkaz (hodnotu adresy) objektu typu MyPerson2 na nespravovaný kód. Pro manipulaci se strukturou, na kterou MyPerson2 odkazuje, vytvoří vzorek vyrovnávací paměť zadané velikosti a vrátí její adresu zkombinováním Marshal.AllocCoTaskMem metod a Marshal.SizeOf metod. V dalším kroku ukázka zkopíruje obsah spravované struktury do nespravované vyrovnávací paměti. Nakonec ukázka používá metodu Marshal.PtrToStructure k zařazování dat z nespravované vyrovnávací paměti do spravovaného objektu a Marshal.FreeCoTaskMem metoda k uvolnění nespravovaného bloku paměti.

Deklarace prototypů

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

Volání funkcí

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 – ukázka

Tato ukázka ukazuje, jak předat strukturu, která obsahuje druhou vloženou strukturu do nespravované funkce. Ukazuje také, jak pomocí atributu MarshalAsAttribute deklarovat pole s pevnou délkou v rámci struktury. V této ukázce jsou vložené prvky struktury přidány do nadřazené struktury. Vzorek vložené struktury, která není zploštěná, naleznete v části Struktury vzorek.

Ukázka FindFile používá následující nespravovanou funkci zobrazenou s původní deklarací funkce:

  • FindFirstFile exportovaný z Kernel32.dll.

    HANDLE FindFirstFile(LPCTSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData);
    

Původní struktura předaná funkci obsahuje následující prvky:

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;

V této ukázce FindData třída obsahuje odpovídající datový člen pro každý prvek původní struktury a vložené struktury. Místo dvou původních vyrovnávacích pamětí znaků třída nahradí řetězce. MarshalAsAttribute nastaví UnmanagedType výčet na ByValTStr, který se používá k identifikaci vložených polí znaků s pevnou délkou, které se zobrazují v rámci nespravovaných struktur.

Třída NativeMethods obsahuje spravovaný prototyp FindFirstFile metody, který předá FindData třídu jako parametr. Parametr musí být deklarován s InAttribute atributy OutAttribute , protože třídy, které jsou odkazové typy, se ve výchozím nastavení předávají jako parametry In.

Deklarace prototypů

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

Volání funkcí

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

sjednocení – ukázka

Tato ukázka ukazuje, jak předat struktury obsahující pouze typy hodnot a struktury obsahující typ hodnoty a řetězec jako parametry nespravované funkce, která očekává sjednocení. Sjednocení představuje umístění paměti, které lze sdílet dvěma nebo více proměnnými.

Vzorek Unions používá následující nespravovanou funkci, která je zobrazena s původní deklarací funkce:

  • TestUnion exportovaný z PinvokeLib.dll

    void TestUnion(MYUNION u, int type);
    

PinvokeLib.dll je vlastní nespravovaná knihovna, která obsahuje implementaci pro dříve uvedenou funkci a dvě sjednocení, MYUNION a MYUNION2. Sjednocení obsahují následující prvky:

union MYUNION
{
    int number;
    double d;
}

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

Ve spravovaném kódu jsou sjednocení definována jako struktury. Struktura MyUnion obsahuje dva typy hodnot jako její členy: celé číslo a double. Atribut StructLayoutAttribute je nastaven tak, aby kontrolovat přesné umístění každého datového členu. Atribut FieldOffsetAttribute poskytuje fyzickou pozici polí v nespravované reprezentaci sjednocení. Všimněte si, že oba členy mají stejné hodnoty posunu, aby členové mohli definovat stejnou část paměti.

MyUnion2_1 a MyUnion2_2 obsahují typ hodnoty (celé číslo) a řetězec. Ve spravovaném kódu se typy hodnot a odkazové typy nesmí překrývat. Tato ukázka používá přetížení metody, které volajícímu umožní používat oba typy při volání stejné nespravované funkce. Rozložení MyUnion2_1 je explicitní a má přesnou hodnotu posunu. Naproti tomu má sekvenční rozložení, MyUnion2_2 protože explicitní rozložení nejsou povolena s odkazovými typy. Atribut MarshalAsAttribute nastaví UnmanagedType výčet byValTStr, který se používá k identifikaci vložených polí znaků s pevnou délkou, které se zobrazují v nespravované reprezentaci sjednocení.

Třída NativeMethods obsahuje prototypy pro metody TestUnion a TestUnion2 metody. TestUnion2 je přetížen pro deklaraci MyUnion2_1 nebo MyUnion2_2 jako parametry.

Deklarace prototypů

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

Volání funkcí

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

Ukázka platformy

V některých scénářích struct se union rozložení můžou lišit v závislosti na cílové platformě. Představte si STRRET například typ, který je definován ve scénáři modelu COM:

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

Výše uvedené struct hlavičky jsou deklarovány pomocí hlaviček systému Windows, které ovlivňují rozložení paměti typu. Při definování ve spravovaném prostředí jsou tyto podrobnosti rozložení potřeba k správné spolupráci s nativním kódem.

Správná spravovaná definice tohoto typu v 32bitovém procesu je:

[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;
}

V 64bitovém procesu se liší velikost a posuny polí. Správné rozložení je:

[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;
}

Selhání správného zvážení nativního rozložení ve scénáři vzájemné spolupráce může vést k náhodným chybám nebo horším, nesprávným výpočtům.

Ve výchozím nastavení mohou sestavení .NET běžet v 32bitové i 64bitové verzi modulu runtime .NET. Aplikace musí počkat na dobu běhu, aby se rozhodla, která z předchozích definic se má použít.

Následující fragment kódu ukazuje příklad výběru mezi 32bitovou a 64bitovou definicí za běhu.

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

SysTime – ukázka

Tato ukázka ukazuje, jak předat ukazatel do třídy nespravované funkci, která očekává ukazatel na strukturu.

Ukázka SysTime používá následující nespravovanou funkci zobrazenou s původní deklarací funkce:

  • GetSystemTime exportovaný z Kernel32.dll

    VOID GetSystemTime(LPSYSTEMTIME lpSystemTime);
    

Původní struktura předaná funkci obsahuje následující prvky:

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

V této ukázce SystemTime třída obsahuje prvky původní struktury reprezentované jako členy třídy. Atribut StructLayoutAttribute je nastaven tak, aby se zajistilo, že jsou členy uspořádány v paměti postupně v pořadí, v jakém se zobrazují.

Třída NativeMethods obsahuje spravovaný prototyp GetSystemTime metody, který ve výchozím nastavení předává SystemTime třídu jako parametr In/Out. Parametr musí být deklarován s InAttribute atributy OutAttribute , protože třídy, které jsou odkazové typy, se ve výchozím nastavení předávají jako parametry In. Aby volající obdržel výsledky, musí být tyto směrové atributy použity explicitně. Třída App vytvoří novou instanci SystemTime třídy a přistupuje k jejím datovým polím.

Vzorky kódu

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 – ukázka

Tato ukázka ukazuje, jak předat pole struktur, které obsahují celá čísla a řetězce jako parametry Out do nespravované funkce.

Tato ukázka ukazuje, jak volat nativní funkci pomocí Marshal třídy a pomocí nebezpečného kódu.

Tato ukázka používá funkce obálky a platformu, které jsou definované v PinvokeLib.dll, které jsou k dispozici také ve zdrojových souborech. Používá TestOutArrayOfStructs funkci a MYSTRSTRUCT2 strukturu. Struktura obsahuje následující prvky:

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

Třída MyStruct obsahuje řetězcový objekt znaků ANSI. Pole CharSet určuje formát ANSI. MyUnsafeStructje struktura obsahující IntPtr typ místo řetězce.

Třída NativeMethods obsahuje přetíženou TestOutArrayOfStructs metodu prototypu. Pokud metoda deklaruje ukazatel jako parametr, třída by měla být označena klíčovým slovem unsafe . Protože Visual Basic nemůže používat nebezpečný kód, přetížená metoda, nebezpečný modifikátor a MyUnsafeStruct struktura jsou zbytečné.

Třída App implementuje metodu UsingMarshaling , která provádí všechny úlohy potřebné k předání pole. Pole je označené klíčovým slovem out (ByRef v jazyce Visual Basic), které označuje, že data předají volajícímu z volaný. Implementace používá následující Marshal metody třídy:

  • PtrToStructure zařazování dat z nespravované vyrovnávací paměti do spravovaného objektu.

  • DestroyStructure uvolnit paměť vyhrazenou pro řetězce ve struktuře.

  • FreeCoTaskMem uvolněte paměť vyhrazenou pro pole.

Jak jsme už zmínili, jazyk C# umožňuje nebezpečný kód a Visual Basic ne. V ukázce jazyka C# je alternativní implementace metody, UsingUnsafePointer která místo třídy používá ukazatele Marshal k předání pole obsahující strukturu MyUnsafeStruct .

Deklarace prototypů

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

Volání funkcí

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

Viz také