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 MyPerson3
MyArrayStruct
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 deMyPerson
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
bevatMyPerson
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
, TestStructInStruct3
en TestArrayInStruct
methoden die door de App
klasse worden aangeroepen. Elk prototype declareert als volgt één parameter:
TestStructInStruct
declareert een verwijzing naar het typeMyPerson2
als parameter.TestStructInStruct3
declareert het typeMyPerson3
als de parameter en geeft de parameter door op waarde.TestArrayInStruct
declareert een verwijzing naar het typeMyArrayStruct
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