Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Klassen und Strukturen sind in .NET Framework ähnlich. Beide können Felder, Eigenschaften und Ereignisse aufweisen. Sie können auch statische und nicht statische Methoden haben. Ein wichtiger Unterschied besteht darin, dass Strukturen Werttypen und Klassen Referenztypen sind.
In der folgenden Tabelle sind Marshallingoptionen für Klassen, Strukturen und Gewerkschaften aufgeführt; beschreibt ihre Nutzung; und stellt einen Link zum entsprechenden Plattform-Aufrufbeispiel bereit.
Typ | BESCHREIBUNG | Beispiel |
---|---|---|
Klasse nach Wert. | Übergibt eine Klasse mit ganzzahligen Membern als In/Out-Parameter, z. B. den verwalteten Fall. | SysTime-Beispiel |
Struktur nach Wert. | Übermittelt Strukturen als Eingabeparameter. | Beispiel für Strukturen |
Struktur nach Bezug. | Übergibt Strukturen als In/Out-Parameter. | OSInfo-Beispiel |
Struktur mit geschachtelten Strukturen (abgeflacht). | Überträgt eine Klasse, die eine Struktur mit geschachtelten Strukturen innerhalb der nicht verwalteten Funktion darstellt. Die Struktur wird im verwalteten Prototyp auf eine einzige große Struktur reduziert. | FindFile-Beispiel |
Struktur mit einem Zeiger auf eine andere Struktur. | Übergibt eine Struktur, die einen Zeiger an eine zweite Struktur als Element enthält. | Beispiel für Strukturen |
Array von Strukturen mit ganzen Zahlen nach Wert. | Übergibt ein Array von Strukturen, die nur ganze Zahlen als In/Out-Parameter enthalten. Elemente des Arrays können geändert werden. | Array-Beispiel |
Array von Strukturen mit ganzen Zahlen und Strings als Referenz. | Übergibt ein Array von Strukturen, die ganze Zahlen und Zeichenfolgen als Out-Parameter enthalten. Die aufgerufene Funktion weist Speicher für das Array zu. | OutArrayOfStructs-Beispiel |
Vereinigungen mit Werttypen. | Übergibt Vereinigungen mit Werttypen (ganze Zahl und Double). | Unions-Beispiel |
Vereinigungen mit gemischten Typen. | Überträgt Vereinigungen mit gemischten Typen (Ganzzahl und Zeichenfolge). | Unions-Beispiel |
Struktur mit plattformspezifischem Layout. | Übergibt einen Typ mit systemeigenen Verpackungsdefinitionen. | Plattformbeispiel |
Nullwerte in der Struktur. | Übergibt einen Nullverweis (Nothing in Visual Basic) anstelle eines Verweises auf einen Werttyp. | HandleRef-Beispiel |
Beispiel für Strukturen
In diesem Beispiel wird veranschaulicht, wie man eine Struktur übergibt, die auf eine zweite Struktur zeigt, wie man eine Struktur mit einer eingebetteten Struktur übergibt und wie man eine Struktur mit einem eingebetteten Array übergibt.
Im Beispiel "Struktur" werden die folgenden nicht verwalteten Funktionen verwendet, die mit der ursprünglichen Funktionsdeklaration angezeigt werden:
TestStructInStruct exportiert aus PinvokeLib.dll.
int TestStructInStruct(MYPERSON2* pPerson2);
TestStructInStruct3 wurde aus PinvokeLib.dllexportiert.
void TestStructInStruct3(MYPERSON3 person3);
TestArrayInStruct exportiert aus PinvokeLib.dll.
void TestArrayInStruct(MYARRAYSTRUCT* pStruct);
PinvokeLib.dll ist eine benutzerdefinierte nicht verwaltete Bibliothek, die Implementierungen für die zuvor aufgeführten Funktionen und vier Strukturen enthält: MYPERSON, MYPERSON2, MYPERSON3 und MYARRAYSTRUCT. Diese Strukturen enthalten die folgenden Elemente:
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;
Die verwalteten MyPerson
, MyPerson2
, , MyPerson3
und MyArrayStruct
Strukturen weisen die folgenden Merkmale auf:
MyPerson
enthält nur Zeichenfolgenmitglieder. Das CharSet-Feld legt die Zeichenfolgen auf das ANSI-Format fest, wenn sie an die nicht verwaltete Funktion übergeben wird.MyPerson2
enthält einen IntPtr für dieMyPerson
Struktur. Der IntPtr-Typ ersetzt den ursprünglichen Zeiger auf die nicht verwaltete Struktur, da .NET Framework-Anwendungen keine Zeiger verwenden, es sei denn, der Code ist als unsicher gekennzeichnet.MyPerson3
enthältMyPerson
als eine eingebettete Struktur. Eine in eine andere Struktur eingebettete Struktur kann vereinfacht werden, indem die Elemente der eingebetteten Struktur direkt in die Hauptstruktur eingefügt werden, oder sie kann wie in diesem Beispiel als eingebettete Struktur verbleiben.MyArrayStruct
enthält ein Array mit ganzen Zahlen. Das MarshalAsAttribute Attribut legt den UnmanagedType Enumerationswert auf ByValArray fest, der verwendet wird, um die Anzahl der Elemente im Array anzugeben.
Für alle Strukturen in diesem Beispiel wird das StructLayoutAttribute Attribut angewendet, um sicherzustellen, dass die Elemente sequenziell im Arbeitsspeicher angeordnet sind, in der Reihenfolge, in der sie angezeigt werden.
Die NativeMethods
-Klasse enthält verwaltete Prototypen für die TestStructInStruct
, TestStructInStruct3
und TestArrayInStruct
Methoden, die von der App
-Klasse aufgerufen werden. Jeder Prototyp deklariert einen einzelnen Parameter wie folgt:
TestStructInStruct
deklariert einen Verweis auf den TypMyPerson2
als Parameter.TestStructInStruct3
deklariert den TypMyPerson3
als Parameter und übergibt den Parameter nach Wert.TestArrayInStruct
deklariert einen Verweis auf den TypMyArrayStruct
als Parameter.
Strukturen als Argumente für Methoden werden nach Wert übergeben, es sei denn, der Parameter enthält das Schlüsselwort Ref (ByRef in Visual Basic). Die Methode TestStructInStruct
übergibt einen Verweis (den Wert einer Adresse) auf ein Objekt vom Typ MyPerson2
an nicht verwalteten Code. Um die Struktur zu manipulieren, auf die MyPerson2
verweist, erstellt das Beispiel einen Puffer mit einer angegebenen Größe und gibt dessen Adresse zurück, indem es die Methoden Marshal.AllocCoTaskMem und Marshal.SizeOf kombiniert. Als Nächstes kopiert das Beispiel den Inhalt der verwalteten Struktur in den nicht verwalteten Puffer. Schließlich verwendet das Beispiel die Marshal.PtrToStructure Methode zum Marshallen von Daten aus dem nicht verwalteten Puffer zu einem verwalteten Objekt und der Marshal.FreeCoTaskMem Methode zum Freigeben des nicht verwalteten Speicherblocks.
Deklarieren von Prototypen
// 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
Aufrufen von Funktionen
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($"{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($"{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-Beispiel
In diesem Beispiel wird veranschaulicht, wie eine Struktur übergeben wird, die eine zweite eingebettete Struktur an eine nicht verwaltete Funktion enthält. Außerdem wird veranschaulicht, wie das MarshalAsAttribute Attribut verwendet wird, um ein Array mit fester Länge innerhalb der Struktur zu deklarieren. In diesem Beispiel werden die eingebetteten Strukturelemente zur übergeordneten Struktur hinzugefügt. Ein Beispiel für eine eingebettete Struktur, die nicht flach ist, finden Sie unter "Structures Sample".
Im FindFile-Beispiel wird die folgende nicht verwaltete Funktion verwendet, die mit der ursprünglichen Funktionsdeklaration angezeigt wird:
FindFirstFile exportiert aus Kernel32.dll.
HANDLE FindFirstFile(LPCTSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData);
Die ursprüngliche Struktur, die an die Funktion übergeben wird, enthält die folgenden Elemente:
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 diesem Beispiel enthält die FindData
Klasse ein entsprechendes Datenelement für jedes Element der ursprünglichen Struktur und der eingebetteten Struktur. Anstelle von zwei ursprünglichen Zeichenpuffern ersetzt die Klasse Zeichenfolgen.
MarshalAsAttribute legt die UnmanagedType Enumeration auf ByValTStr fest, die verwendet wird, um Inline-Zeichenarrays mit fester Länge zu identifizieren, die in den nicht verwalteten Strukturen vorkommen.
Die NativeMethods
Klasse enthält einen verwalteten Prototyp der FindFirstFile
Methode, der die FindData
Klasse als Parameter übergibt. Der Parameter muss mit den Attributen InAttribute und OutAttribute deklariert werden, da Klassen, die Referenztypen sind, standardmäßig als In-Parameter übergeben werden.
Deklarieren von Prototypen
// 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
Aufrufen von Funktionen
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
Unions-Beispiel
In diesem Beispiel wird veranschaulicht, wie Strukturen übergeben werden, die nur Werttypen enthalten, und Strukturen, die einen Werttyp und eine Zeichenfolge als Parameter an eine nicht verwaltete Funktion übergeben, die eine Union erwartet. Eine Union repräsentiert einen Speicherort, der von zwei oder mehr Variablen gemeinsam genutzt werden kann.
Im Beispiel "Unions" wird die folgende nicht verwaltete Funktion verwendet, die mit der ursprünglichen Funktionsdeklaration dargestellt wird:
TestUnion wurde aus PinvokeLib.dllexportiert.
void TestUnion(MYUNION u, int type);
PinvokeLib.dll ist eine benutzerdefinierte nicht verwaltete Bibliothek, die eine Implementierung für die zuvor aufgeführte Funktion und zwei Gewerkschaften, MYUNION und MYUNION2 enthält. Die Gewerkschaften enthalten die folgenden Elemente:
union MYUNION
{
int number;
double d;
}
union MYUNION2
{
int i;
char str[128];
};
Im verwalteten Code werden Gewerkschaften als Strukturen definiert. Die MyUnion
Struktur enthält zwei Werttypen als Member: eine ganze Zahl und eine Doppelzahl. Das StructLayoutAttribute Attribut wird so festgelegt, dass die genaue Position jedes Datenelements gesteuert wird. Das FieldOffsetAttribute Attribut stellt die physische Position von Feldern innerhalb der nicht verwalteten Darstellung einer Vereinigung bereit. Beachten Sie, dass beide Member über dieselben Offsetwerte verfügen, sodass die Elemente denselben Speicherabschnitt definieren können.
MyUnion2_1
und MyUnion2_2
enthalten einen Werttyp (ganze Zahl) bzw. eine Zeichenfolge. In verwaltetem Code dürfen Werttypen und Verweistypen nicht überlappen. In diesem Beispiel wird Methodenüberladung verwendet, um dem Aufrufer die Verwendung beider Typen beim Aufrufen derselben nicht verwalteten Funktion zu ermöglichen. Das Layout von MyUnion2_1
ist explizit und weist einen genauen Offsetwert auf. Weist dagegen MyUnion2_2
ein sequenzielles Layout auf, da explizite Layouts nicht mit Verweistypen zulässig sind. Das MarshalAsAttribute Attribut legt die UnmanagedType Enumeration auf ByValTStr fest, die verwendet wird, um die Inline-Zeichenarrays mit fester Länge zu identifizieren, die in der nicht verwalteten Darstellung der Union angezeigt werden.
Die NativeMethods
Klasse enthält die Prototypen für die TestUnion
und TestUnion2
Methoden.
TestUnion2
wird überladen, um MyUnion2_1
oder MyUnion2_2
als Parameter zu deklarieren.
Deklarieren von Prototypen
// 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
Aufrufen von Funktionen
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
Plattformbeispiel
In einigen Szenarien können sich struct
- und union
-Layouts je nach Zielplattform unterscheiden. Berücksichtigen Sie beispielsweise den STRRET
Typ, wenn er in einem COM-Szenario definiert ist:
#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>
Die obigen struct
Werden mit Windows-Headern deklariert, die das Speicherlayout des Typs beeinflussen. Wenn sie in einer verwalteten Umgebung definiert sind, sind diese Layoutdetails erforderlich, um ordnungsgemäß mit systemeigenem Code zu arbeiten.
Die richtige verwaltete Definition dieses Typs in einem 32-Bit-Prozess lautet:
[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;
}
Bei einem 64-Bit-Prozess unterscheiden sich die Größen- und Feldoffsets. Das richtige Layout ist:
[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;
}
Wenn das systemeigene Layout in einem Interoperabilitätsszenario nicht ordnungsgemäß berücksichtigt wird, kann dies zu zufälligen Abstürzen oder schlimmeren fehlerhaften Berechnungen führen.
Standardmäßig können .NET-Assemblys sowohl in einer 32-Bit- als auch in einer 64-Bit-Version der .NET-Laufzeit ausgeführt werden. Die App muss bis zur Laufzeit warten, um zu entscheiden, welche der vorherigen Definitionen verwendet werden sollen.
Der folgende Codeausschnitt zeigt ein Beispiel für die Auswahl zwischen der 32-Bit- und der 64-Bit-Definition zur Laufzeit.
if (IntPtr.Size == 8)
{
// Use the STRRET_64 definition
}
else
{
Debug.Assert(IntPtr.Size == 4);
// Use the STRRET_32 definition
}
SysTime-Beispiel
In diesem Beispiel wird veranschaulicht, wie ein Zeiger an eine Klasse an eine nicht verwaltete Funktion übergeben wird, die einen Zeiger auf eine Struktur erwartet.
Im SysTime-Beispiel wird die folgende nicht verwaltete Funktion verwendet, die mit der ursprünglichen Funktionsdeklaration angezeigt wird:
GetSystemTime wurde aus Kernel32.dllexportiert.
VOID GetSystemTime(LPSYSTEMTIME lpSystemTime);
Die ursprüngliche Struktur, die an die Funktion übergeben wird, enthält die folgenden Elemente:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;
In diesem Beispiel enthält die SystemTime
Klasse die Elemente der ursprünglichen Struktur, die als Klassenmitglieder dargestellt werden. Das StructLayoutAttribute Attribut wird festgelegt, um sicherzustellen, dass die Elemente sequenziell im Arbeitsspeicher angeordnet sind, in der Reihenfolge, in der sie angezeigt werden.
Die NativeMethods
Klasse enthält einen verwalteten Prototyp der GetSystemTime
Methode, der die SystemTime
Klasse standardmäßig als In/Out-Parameter übergibt. Der Parameter muss mit den Attributen InAttribute und OutAttribute deklariert werden, da Klassen, die Referenztypen sind, standardmäßig als In-Parameter übergeben werden. Damit der Aufrufer die Ergebnisse empfängt, müssen diese direktionalen Attribute explizit angewendet werden. Die App
Klasse erstellt eine neue Instanz der SystemTime
Klasse und greift auf ihre Datenfelder zu.
Codebeispiele
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-Beispiel
In diesem Beispiel wird gezeigt, wie Sie ein Array von Strukturen übergeben, das ganze Zahlen und Zeichenfolgen als Out-Parameter an eine nicht verwaltete Funktion enthält.
In diesem Beispiel wird veranschaulicht, wie eine systemeigene Funktion mithilfe der Marshal Klasse und mithilfe unsicherer Code aufgerufen wird.
In diesem Beispiel werden eine Wrapperfunktion und plattformübergreifende Aufrufe verwendet, die in PinvokeLib.dlldefiniert sind, die auch in den Quelldateien bereitgestellt werden. Sie verwendet die TestOutArrayOfStructs
Funktion und die MYSTRSTRUCT2
Struktur. Die Struktur enthält die folgenden Elemente:
typedef struct _MYSTRSTRUCT2
{
char* buffer;
UINT size;
} MYSTRSTRUCT2;
Die MyStruct
Klasse enthält ein Zeichenfolgenobjekt mit ANSI-Zeichen. Das CharSet Feld gibt das ANSI-Format an.
MyUnsafeStruct
ist eine Struktur, die einen IntPtr Typ anstelle einer Zeichenfolge enthält.
Die NativeMethods
Klasse enthält die überladene TestOutArrayOfStructs
Prototypmethode. Wenn eine Methode einen Zeiger als Parameter deklariert, sollte die Klasse mit dem unsafe
Schlüsselwort gekennzeichnet werden. Da Visual Basic keinen unsicheren Code verwenden kann, sind die überladene Methode, der unsichere Modifizierer und die MyUnsafeStruct
Struktur nicht erforderlich.
Die App
Klasse implementiert die UsingMarshaling
Methode, die alle zum Übergeben des Arrays erforderlichen Aufgaben ausführt. Das Array ist mit dem out
Schlüsselwort (ByRef
in Visual Basic) gekennzeichnet, um anzugeben, dass Daten vom Angerufenen an den Aufrufer übergeben werden. Die Implementierung verwendet die folgenden Marshal Klassenmethoden:
PtrToStructure zum Marshallen von Daten aus dem nicht verwalteten Puffer in ein verwaltetes Objekt.
DestroyStructure um den Speicher freizugeben, der für Zeichenfolgen in der Struktur reserviert ist.
FreeCoTaskMem um den für das Array reservierten Speicher freizugeben.
Wie bereits erwähnt, lässt C# unsicheren Code zu, aber Visual Basic nicht. Im C#-Beispiel ist UsingUnsafePointer
eine alternative Methodenimplementierung, die Zeiger anstelle der Marshal Klasse verwendet, um das Array, das die MyUnsafeStruct
Struktur enthält, zurückzugeben.
Deklarieren von Prototypen
// 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
Aufrufen von Funktionen
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 {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 {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