Rangeringsklasser, strukturer och fackföreningar
Klasser och strukturer liknar dem i .NET Framework. Båda kan ha fält, egenskaper och händelser. De kan också ha statiska och icke-statiska metoder. En viktig skillnad är att strukturer är värdetyper och klasser är referenstyper.
I följande tabell visas rangeringsalternativ för klasser, strukturer och fackföreningar. beskriver deras användning. och tillhandahåller en länk till motsvarande plattform som anropar exempel.
Typ | Beskrivning | Exempel |
---|---|---|
Klassificera efter värde. | Skickar en klass med heltalsmedlemmar som en In/Out-parameter, som det hanterade fallet. | SysTime-exempel |
Struktur efter värde. | Skickar strukturer som In-parametrar. | Exempel på strukturer |
Struktur efter referens. | Skickar strukturer som in-/ut-parametrar. | OSInfo-exempel |
Struktur med kapslade strukturer (utplattade). | Skickar en klass som representerar en struktur med kapslade strukturer i den ohanterade funktionen. Strukturen planas ut till en enda stor struktur i den hanterade prototypen. | FindFile-exempel |
Struktur med en pekare till en annan struktur. | Skickar en struktur som innehåller en pekare till en andra struktur som medlem. | Exempel på strukturer |
Matris med strukturer med heltal efter värde. | Skickar en matris med strukturer som endast innehåller heltal som en In/Out-parameter. Medlemmar i matrisen kan ändras. | Matrisexempel |
Matris med strukturer med heltal och strängar efter referens. | Skickar en matris med strukturer som innehåller heltal och strängar som en Out-parameter. Den anropade funktionen allokerar minne för matrisen. | OutArrayOfStructs-exempel |
Unioner med värdetyper. | Skickar fackföreningar med värdetyper (heltal och dubbel). | Unionsexempel |
Unioner med blandade typer. | Skickar unioner med blandade typer (heltal och sträng). | Unionsexempel |
Struct med plattformsspecifik layout. | Skickar en typ med inbyggda förpackningsdefinitioner. | Plattformsexempel |
Null-värden i strukturen. | Skickar en null-referens (Ingenting i Visual Basic) i stället för en referens till en värdetyp. | HandleRef-exempel |
Exempel på strukturer
Det här exemplet visar hur du skickar en struktur som pekar på en andra struktur, skickar en struktur med en inbäddad struktur och skickar en struktur med en inbäddad matris.
Structs-exemplet använder följande ohanterade funktioner som visas med den ursprungliga funktionsdeklarationen:
TestStructInStruct exporteras från PinvokeLib.dll.
int TestStructInStruct(MYPERSON2* pPerson2);
TestStructInStruct3 exporteras från PinvokeLib.dll.
void TestStructInStruct3(MYPERSON3 person3);
TestArrayInStruct exporteras från PinvokeLib.dll.
void TestArrayInStruct(MYARRAYSTRUCT* pStruct);
PinvokeLib.dll är ett anpassat ohanterat bibliotek som innehåller implementeringar för tidigare listade funktioner och fyra strukturer: MYPERSON, MYPERSON2, MYPERSON3 och MYARRAYSTRUCT. Dessa strukturer innehåller följande element:
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 hanterade MyPerson
strukturerna , MyPerson2
, MyPerson3
och MyArrayStruct
har följande egenskap:
MyPerson
innehåller endast strängmedlemmar. Fältet CharSet anger strängarna till ANSI-format när de skickas till den ohanterade funktionen.MyPerson2
innehåller en IntPtr tillMyPerson
strukturen. IntPtr-typen ersätter den ursprungliga pekaren mot den ohanterade strukturen eftersom .NET Framework-program inte använder pekare om inte koden har markerats som osäker.MyPerson3
innehållerMyPerson
som en inbäddad struktur. En struktur som är inbäddad i en annan struktur kan plattas ut genom att placera elementen i den inbäddade strukturen direkt i huvudstrukturen, eller så kan den lämnas som en inbäddad struktur, vilket görs i det här exemplet.MyArrayStruct
innehåller en matris med heltal. Attributet MarshalAsAttribute anger UnmanagedType uppräkningsvärdet till ByValArray, som används för att ange antalet element i matrisen.
För alla strukturer i det här exemplet StructLayoutAttribute tillämpas attributet för att säkerställa att medlemmarna ordnas i minnet sekventiellt i den ordning de visas.
Klassen NativeMethods
innehåller hanterade prototyper för TestStructInStruct
metoderna , TestStructInStruct3
och som TestArrayInStruct
anropas av App
klassen. Varje prototyp deklarerar en enskild parameter enligt följande:
TestStructInStruct
deklarerar en referens till typenMyPerson2
som dess parameter.TestStructInStruct3
deklarerar typenMyPerson3
som dess parameter och skickar parametern efter värde.TestArrayInStruct
deklarerar en referens till typenMyArrayStruct
som dess parameter.
Strukturer som argument till metoder skickas efter värde om inte parametern innehåller nyckelordet ref (ByRef i Visual Basic). Metoden skickar till exempel TestStructInStruct
en referens (värdet för en adress) till ett objekt av typen MyPerson2
till ohanterad kod. Om du vill ändra strukturen som MyPerson2
pekar på skapar exemplet en buffert av en angiven storlek och returnerar dess adress genom att kombinera Marshal.AllocCoTaskMem metoderna och Marshal.SizeOf . Sedan kopierar exemplet innehållet i den hanterade strukturen till den ohanterade bufferten. Slutligen använder Marshal.PtrToStructure exemplet metoden för att konvertera data från den ohanterade bufferten Marshal.FreeCoTaskMem till ett hanterat objekt och metoden för att frigöra det ohanterade minnesblocket.
Deklarera prototyper
// 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
Anropa funktioner
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-exempel
Det här exemplet visar hur du skickar en struktur som innehåller en andra inbäddad struktur till en ohanterad funktion. Den visar också hur du använder MarshalAsAttribute attributet för att deklarera en matris med fast längd i strukturen. I det här exemplet läggs de inbäddade strukturelementen till i den överordnade strukturen. Ett exempel på en inbäddad struktur som inte är utplattad finns i Strukturexempel.
FindFile-exemplet använder följande ohanterade funktion som visas med dess ursprungliga funktionsdeklaration:
FindFirstFile exporteras från Kernel32.dll.
HANDLE FindFirstFile(LPCTSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData);
Den ursprungliga strukturen som skickades till funktionen innehåller följande element:
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;
I det här exemplet FindData
innehåller klassen en motsvarande datamedlem för varje element i den ursprungliga strukturen och den inbäddade strukturen. I stället för två ursprungliga teckenbuffertar ersätter klassen strängar. MarshalAsAttribute anger UnmanagedType uppräkningen till ByValTStr, som används för att identifiera de infogade teckenmatriser med fast längd som visas i de ohanterade strukturerna.
Klassen NativeMethods
innehåller en hanterad prototyp av FindFirstFile
metoden, som skickar FindData
klassen som en parameter. Parametern måste deklareras med attributen InAttribute och OutAttribute eftersom klasser, som är referenstyper, skickas som In-parametrar som standard.
Deklarera prototyper
// 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
Anropa funktioner
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
Unionsexempel
Det här exemplet visar hur du skickar strukturer som endast innehåller värdetyper och strukturer som innehåller en värdetyp och en sträng som parametrar till en ohanterad funktion som förväntar sig en union. En union representerar en minnesplats som kan delas av två eller flera variabler.
Unionsexemplet använder följande ohanterade funktion, som visas med dess ursprungliga funktionsdeklaration:
TestUnion exporteras från PinvokeLib.dll.
void TestUnion(MYUNION u, int type);
PinvokeLib.dll är ett anpassat ohanterat bibliotek som innehåller en implementering för den tidigare listade funktionen och två fackföreningar, MYUNION och MYUNION2. Fackföreningarna innehåller följande element:
union MYUNION
{
int number;
double d;
}
union MYUNION2
{
int i;
char str[128];
};
I hanterad kod definieras fackföreningar som strukturer. Strukturen MyUnion
innehåller två värdetyper som medlemmar: ett heltal och en dubbel. Attributet StructLayoutAttribute är inställt på att styra den exakta positionen för varje datamedlem. Attributet FieldOffsetAttribute ger den fysiska positionen för fält inom den ohanterade representationen av en union. Observera att båda medlemmarna har samma förskjutningsvärden, så att medlemmarna kan definiera samma minnesbit.
MyUnion2_1
och MyUnion2_2
innehåller en värdetyp (heltal) respektive en sträng. I hanterad kod får värdetyper och referenstyper inte överlappa varandra. Det här exemplet använder metodöverlagring för att göra det möjligt för anroparen att använda båda typerna när samma ohanterade funktion anropas. Layouten för MyUnion2_1
är explicit och har ett exakt förskjutningsvärde. Däremot MyUnion2_2
har en sekventiell layout eftersom explicita layouter inte tillåts med referenstyper. Attributet MarshalAsAttribute anger UnmanagedType uppräkningen till ByValTStr, som används för att identifiera de infogade, fasta teckenmatriserna som visas inom den ohanterade representationen av unionen.
Klassen NativeMethods
innehåller prototyperna för TestUnion
metoderna och TestUnion2
. TestUnion2
är överbelastad för att deklarera MyUnion2_1
eller MyUnion2_2
som parametrar.
Deklarera prototyper
// 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
Anropa funktioner
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
Plattformsexempel
I vissa scenarier struct
union
kan layouterna variera beroende på målplattformen. Tänk till exempel på typen när den STRRET
definieras i ett 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>
Ovanstående struct
deklareras med Windows-huvuden som påverkar typens minneslayout. När den definieras i en hanterad miljö behövs den här layoutinformationen för att fungera korrekt med inbyggd kod.
Rätt hanterad definition av den här typen i en 32-bitarsprocess är:
[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;
}
Vid en 64-bitarsprocess skiljer sig storleks- och fältförskjutningarna. Rätt layout är:
[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;
}
Om den inbyggda layouten inte kan beaktas korrekt i ett interop-scenario kan det leda till slumpmässiga krascher eller sämre, felaktiga beräkningar.
Som standard kan .NET-sammansättningar köras i både en 32-bitars- och 64-bitarsversion av .NET-körningen. Appen måste vänta tills körningstiden för att bestämma vilka av de tidigare definitionerna som ska användas.
Följande kodfragment visar ett exempel på hur du väljer mellan 32-bitars- och 64-bitarsdefinitionen vid körning.
if (IntPtr.Size == 8)
{
// Use the STRRET_64 definition
}
else
{
Debug.Assert(IntPtr.Size == 4);
// Use the STRRET_32 definition
}
SysTime-exempel
Det här exemplet visar hur du skickar en pekare till en klass till en ohanterad funktion som förväntar sig en pekare till en struktur.
SysTime-exemplet använder följande ohanterade funktion som visas med den ursprungliga funktionsdeklarationen:
GetSystemTime exporteras från Kernel32.dll.
VOID GetSystemTime(LPSYSTEMTIME lpSystemTime);
Den ursprungliga strukturen som skickades till funktionen innehåller följande element:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;
I det här exemplet SystemTime
innehåller klassen elementen i den ursprungliga strukturen som representeras som klassmedlemmar. Attributet StructLayoutAttribute är inställt för att säkerställa att medlemmarna ordnas i minnet sekventiellt i den ordning de visas.
Klassen NativeMethods
innehåller en hanterad prototyp av GetSystemTime
metoden, som skickar SystemTime
klassen som in-/ut-parameter som standard. Parametern måste deklareras med attributen InAttribute och OutAttribute eftersom klasser, som är referenstyper, skickas som In-parametrar som standard. För att anroparen ska få resultaten måste dessa riktningsattribut tillämpas explicit. Klassen App
skapar en ny instans av SystemTime
klassen och kommer åt dess datafält.
Kodexempel
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-exempel
Det här exemplet visar hur du skickar en matris med strukturer som innehåller heltal och strängar som Out-parametrar till en ohanterad funktion.
Det här exemplet visar hur du anropar en intern funktion med hjälp Marshal av klassen och med hjälp av osäker kod.
I det här exemplet används omslutningsfunktioner och plattformsanrop som definierats i PinvokeLib.dll, som också anges i källfilerna. Den använder TestOutArrayOfStructs
funktionen och MYSTRSTRUCT2
strukturen. Strukturen innehåller följande element:
typedef struct _MYSTRSTRUCT2
{
char* buffer;
UINT size;
} MYSTRSTRUCT2;
Klassen MyStruct
innehåller ett strängobjekt med ANSI-tecken. Fältet CharSet anger ANSI-format. MyUnsafeStruct
, är en struktur som innehåller en IntPtr typ i stället för en sträng.
Klassen NativeMethods
innehåller den överlagrade TestOutArrayOfStructs
prototypmetoden. Om en metod deklarerar en pekare som en parameter ska klassen markeras med nyckelordet unsafe
. Eftersom Visual Basic inte kan använda osäker kod är den överlagrade metoden, den osäkra modifieraren och MyUnsafeStruct
strukturen onödig.
Klassen App
implementerar UsingMarshaling
metoden, som utför alla uppgifter som krävs för att skicka matrisen. Matrisen är markerad med nyckelordet out
(ByRef
i Visual Basic) för att indikera att data skickas från anropare till anropare. Implementeringen använder följande Marshal klassmetoder:
PtrToStructure för att konvertera data från den ohanterade bufferten till ett hanterat objekt.
DestroyStructure för att frigöra det minne som är reserverat för strängar i strukturen.
FreeCoTaskMem för att frigöra det minne som är reserverat för matrisen.
Som tidigare nämnts tillåter C# osäker kod och Visual Basic gör det inte. I C#-exemplet UsingUnsafePointer
är en alternativ metodimplementering som använder pekare i stället för Marshal klassen för att skicka tillbaka matrisen MyUnsafeStruct
som innehåller strukturen.
Deklarera prototyper
// 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
Anropa funktioner
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