Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Kelas dan struktur serupa dalam .NET Framework. Keduanya dapat memiliki bidang, properti, dan peristiwa. Mereka juga dapat memiliki metode statis dan nonstatis. Salah satu perbedaan penting adalah bahwa struktur adalah jenis nilai dan kelas adalah jenis referensi.
Tabel berikut ini mencantumkan opsi marshalling untuk kelas, struktur, dan union; menjelaskan penggunaannya; dan menyediakan tautan ke contoh pemanggilan platform yang sesuai.
Tipe | Deskripsi | Contoh |
---|---|---|
Kelas berdasarkan nilai. | Mengoperkan kelas dengan anggota integer sebagai parameter masuk/keluar, seperti dalam kasus terkelola. | Sampel SysTime |
Struktur menurut nilai. | Meneruskan struktur sebagai parameter 'In'. | Struktur sampel |
Struktur menurut referensi. | Meneruskan struktur dalam bentuk parameter masuk/keluar. | Sampel OSInfo |
Struktur dengan struktur berlapis (diratakan). | Meneruskan kelas yang mewakili struktur dengan struktur berlapis dalam fungsi yang tidak dikelola. Struktur diratakan menjadi satu struktur besar dalam prototipe yang dikelola. | Sampel FindFile |
Struktur dengan penunjuk ke struktur lain. | Meneruskan suatu struktur yang berisi pointer ke struktur kedua sebagai bagian anggota. | Sampel Struktur |
Array struktur dengan bilangan bulat menurut nilai. | Mengoper array dari struktur yang hanya berisi bilangan bulat sebagai parameter Masuk/Keluar. Anggota array dapat diubah. | Array Sampel |
Array struktur dengan bilangan bulat dan string berdasarkan referensi. | Meneruskan array struktur yang berisi bilangan bulat dan string sebagai parameter 'out'. Fungsi yang dipanggil mengalokasikan memori untuk array. | Sampel OutArrayOfStructs |
Serikat dengan jenis nilai. | Meneruskan gabungan dengan jenis nilai (integer dan double). | Sampel serikat |
Serikat dengan jenis campuran. | Meneruskan gabungan dengan jenis campuran (bilangan bulat dan string). | Sampel serikat |
Struktur dengan tata letak yang spesifik untuk platform. | Meneruskan jenis dengan definisi pengemasan asli. | Sampel platform |
Nilai null dalam struktur. | Meneruskan referensi kosong (Tidak ada di Visual Basic) alih-alih referensi ke tipe nilai. | Sampel HandleRef |
Sampel struktur
Contoh ini menunjukkan cara meneruskan struktur yang mengarah ke struktur kedua, meneruskan struktur yang memiliki struktur yang disematkan, dan meneruskan struktur yang memiliki array yang disematkan.
Sampel Structs menggunakan fungsi tidak terkelola berikut, yang ditunjukkan dengan deklarasi fungsi aslinya:
TestStructInStruct telah diekspor dari PinvokeLib.dll.
int TestStructInStruct(MYPERSON2* pPerson2);
TestStructInStruct3 diekspor dari PinvokeLib.dll.
void TestStructInStruct3(MYPERSON3 person3);
TestArrayInStruct diekspor dari PinvokeLib.dll.
void TestArrayInStruct(MYARRAYSTRUCT* pStruct);
PinvokeLib.dll adalah pustaka kustom yang tidak dikelola yang berisi implementasi untuk fungsi yang tercantum sebelumnya dan empat struktur: MYPERSON, MYPERSON2, MYPERSON3, dan MYARRAYSTRUCT. Struktur ini berisi elemen-elemen berikut:
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;
Struktur MyPerson
terkelola , MyPerson2
, MyPerson3
, dan MyArrayStruct
memiliki karakteristik berikut:
MyPerson
hanya berisi elemen string. Bidang CharSet mengatur string ke format ANSI saat diteruskan ke fungsi yang tidak dikelola.MyPerson2
berisi IntPtr ke strukturMyPerson
. Jenis IntPtr menggantikan pointer asli ke struktur yang tidak dikelola karena aplikasi .NET Framework tidak menggunakan pointer kecuali kode ditandai tidak aman.MyPerson3
berisiMyPerson
sebagai struktur yang disematkan. Struktur yang disematkan dalam struktur lain dapat diratakan dengan menempatkan elemen struktur tertanam langsung ke struktur utama, atau dapat dibiarkan sebagai struktur tertanam, seperti yang dilakukan dalam sampel ini.MyArrayStruct
berisi array bilangan bulat. Atribut MarshalAsAttribute mengatur UnmanagedType nilai enumerasi ke ByValArray, yang digunakan untuk menunjukkan jumlah elemen dalam array.
Untuk semua struktur dalam sampel ini, atribut StructLayoutAttribute diterapkan untuk memastikan bahwa anggota diatur dalam memori secara berurutan, dalam urutan di mana mereka muncul.
Kelas NativeMethods
berisi prototipe terkelola untuk metode TestStructInStruct
, TestStructInStruct3
, dan TestArrayInStruct
yang dipanggil oleh kelas App
. Setiap prototipe mendeklarasikan satu parameter, sebagai berikut:
TestStructInStruct
mendeklarasikan referensi ke tipeMyPerson2
sebagai parameternya.TestStructInStruct3
menyatakan jenisMyPerson3
sebagai parameternya dan meneruskan parameter berdasarkan nilai.TestArrayInStruct
mendeklarasikan referensi ke tipeMyArrayStruct
sebagai parameternya.
Struktur dilewatkan ke metode sebagai argumen berdasarkan nilai kecuali parameter berisi kata kunci ref (ByRef in Visual Basic). Misalnya, metode TestStructInStruct
meneruskan referensi (nilai alamat) ke objek jenis MyPerson2
ke kode yang tidak dikelola. Untuk memanipulasi struktur yang diarahkan oleh MyPerson2
, sampel membuat buffer dengan ukuran tertentu dan mengembalikan alamat buffer tersebut dengan menggabungkan metode Marshal.AllocCoTaskMem dan Marshal.SizeOf. Selanjutnya, sampel menyalin konten struktur terkelola ke buffer yang tidak dikelola. Terakhir, sampel menggunakan metode Marshal.PtrToStructure untuk marshal data dari buffer yang tidak dikelola ke objek terkelola dan metode Marshal.FreeCoTaskMem untuk membebaskan blok memori yang tidak dikelola.
Mendeklarasikan Prototipe
// 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
Fungsi Panggilan
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
Sampel FindFile
Sampel ini menunjukkan cara meneruskan struktur yang memuat struktur lain yang disematkan ke fungsi yang tidak terkelola. Ini juga menunjukkan cara menggunakan atribut MarshalAsAttribute untuk mendeklarasikan array panjang tetap dalam struktur. Dalam sampel ini, elemen struktur yang disematkan ditambahkan ke struktur induk. Untuk sampel struktur yang disematkan yang tidak diratakan, lihat Sampel Struktur.
Sampel FindFile menggunakan fungsi tidak terkelola berikut, yang ditunjukkan dengan deklarasi fungsi aslinya:
Fungsi FindFirstFile diekspor dari Kernel32.dll.
HANDLE FindFirstFile(LPCTSTR lpFileName, LPWIN32_FIND_DATA lpFindFileData);
Struktur asli yang diteruskan ke fungsi berisi elemen-elemen berikut:
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;
Dalam sampel ini, kelas FindData
berisi anggota data yang sesuai untuk setiap elemen struktur asli dan struktur yang disematkan. Sebagai pengganti dua buffer karakter asli, kelas menggantinya dengan string.
MarshalAsAttribute mengatur UnmanagedType enumerasi ke ByValTStr, yang digunakan untuk mengidentifikasi array karakter panjang tetap sebaris yang muncul dalam struktur yang tidak dikelola.
Kelas NativeMethods
berisi prototipe terkelola dari metode FindFirstFile
, yang melewati kelas FindData
sebagai parameter. Parameter harus dideklarasikan dengan atribut InAttribute dan OutAttribute karena kelas, yang merupakan tipe referensi, secara default diteruskan sebagai parameter In.
Mendeklarasikan Prototipe
// 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
Fungsi Panggilan
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
Sampel unions
Sampel ini menunjukkan cara meneruskan struktur yang hanya berisi jenis nilai, dan struktur yang berisi jenis nilai dan string sebagai parameter ke fungsi unmanaged yang mengharapkan union. Union mewakili lokasi memori yang dapat dibagikan oleh dua atau lebih variabel.
Sampel Unions menggunakan fungsi tidak terkelola berikut, yang ditunjukkan dengan deklarasi fungsi aslinya:
TestUnion diekspor dari PinvokeLib.dll.
void TestUnion(MYUNION u, int type);
PinvokeLib.dll adalah pustaka kustom yang tidak dikelola yang berisi implementasi untuk fungsi yang tercantum sebelumnya dan dua serikat, MYUNION dan MYUNION2. Penyatuan berisi elemen berikut:
union MYUNION
{
int number;
double d;
}
union MYUNION2
{
int i;
char str[128];
};
Dalam kode terkelola, serikat didefinisikan sebagai struktur. Struktur MyUnion
berisi dua jenis nilai sebagai anggotanya: bilangan bulat dan ganda. Atribut StructLayoutAttribute diatur untuk mengontrol posisi yang tepat dari setiap anggota data. Atribut FieldOffsetAttribute menyediakan posisi fisik field dalam representasi tak dikelola dari sebuah union. Perhatikan bahwa kedua anggota memiliki nilai offset yang sama, sehingga anggota dapat menentukan bagian memori yang sama.
MyUnion2_1
dan MyUnion2_2
masing-masing berisi jenis nilai (bilangan bulat) dan string. Dalam kode terkelola, jenis nilai dan jenis referensi tidak diizinkan untuk tumpang tindih. Sampel ini menggunakan kelebihan beban metode untuk memungkinkan pemanggil menggunakan kedua jenis saat memanggil fungsi yang tidak dikelola yang sama. Tata letak MyUnion2_1
eksplisit dan memiliki nilai offset yang tepat. Sebaliknya, MyUnion2_2
memiliki tata letak berurutan, karena tata letak eksplisit tidak diizinkan dengan jenis referensi. Atribut MarshalAsAttribute mengatur UnmanagedType enumerasi ke ByValTStr, yang digunakan untuk mengidentifikasi array karakter sebaris dengan panjang tetap yang muncul dalam representasi yang tidak dikelola dari union.
Kelas NativeMethods
berisi prototipe untuk metode TestUnion
dan TestUnion2
.
TestUnion2
kelebihan beban untuk mendeklarasikan MyUnion2_1
atau MyUnion2_2
sebagai parameter.
Mendeklarasikan Prototipe
// 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
Fungsi Panggilan
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
Platform sampel
Dalam beberapa skenario, tata letak struct
dan union
dapat berbeda tergantung pada platform yang ditargetkan. Misalnya, pertimbangkan tipe STRRET
ketika didefinisikan dalam skenario COM.
#include <pshpack8.h> /* Defines the packing of the struct */
typedef struct _STRRET
{
UINT uType;
/* [switch_is][switch_type] */ union
{
/* [case()][string] */ LPWSTR pOleStr;
/* [case()] */ UINT uOffset;
/* [case()] */ char cStr[ 260 ];
} DUMMYUNIONNAME;
} STRRET;
#include <poppack.h>
struct
di atas dideklarasikan dengan header Windows yang memengaruhi tata letak memori dari jenis tersebut. Ketika didefinisikan dalam lingkungan terkelola, detail tata letak ini diperlukan untuk beroperasi dengan benar dengan kode asli.
Definisi terkelola yang benar dari jenis ini dalam proses 32-bit adalah:
[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;
}
Pada proses 64-bit, ukuran dan offset bidang berbeda. Tata letak yang benar adalah:
[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;
}
Kegagalan untuk mempertimbangkan tata letak asli dengan benar dalam skenario interop dapat mengakibatkan crash acak atau komputasi yang lebih buruk dan salah.
Secara default, rakitan .NET dapat berjalan dalam versi 32-bit dan 64-bit dari runtime .NET. Aplikasi harus menunggu hingga run time untuk memutuskan definisi mana yang akan digunakan sebelumnya.
Cuplikan kode berikut menunjukkan contoh cara memilih antara definisi 32-bit dan 64-bit pada waktu proses.
if (IntPtr.Size == 8)
{
// Use the STRRET_64 definition
}
else
{
Debug.Assert(IntPtr.Size == 4);
// Use the STRRET_32 definition
}
Sampel SysTime
Sampel ini menunjukkan cara meneruskan penunjuk ke kelas ke fungsi yang tidak dikelola yang mengharapkan penunjuk ke struktur.
Sampel SysTime menggunakan fungsi tidak terkelola berikut, yang ditunjukkan dengan deklarasi fungsi aslinya:
GetSystemTime diekspor dari Kernel32.dll.
VOID GetSystemTime(LPSYSTEMTIME lpSystemTime);
Struktur asli yang diteruskan ke fungsi berisi elemen-elemen berikut:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME;
Dalam sampel ini, kelas SystemTime
berisi elemen struktur asli yang diwakili sebagai anggota kelas. Atribut StructLayoutAttribute diatur untuk memastikan bahwa anggota diatur dalam memori secara berurutan, dalam urutan di mana mereka muncul.
Kelas NativeMethods
berisi prototipe terkelola dari metode GetSystemTime
, yang meneruskan kelas SystemTime
sebagai parameter Masuk/Keluar secara default. Parameter harus dideklarasikan dengan atribut InAttribute dan OutAttribute karena kelas, yang merupakan tipe referensi, secara default diteruskan sebagai parameter In. Agar penelepon menerima hasil, atribut arah ini harus diterapkan secara eksplisit. Kelas App
membuat instans baru kelas SystemTime
dan mengakses bidang datanya.
Kode Sampel
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
Contoh OutArrayOfStructs
Sampel ini menunjukkan cara meneruskan array struktur yang berisi bilangan bulat dan string sebagai parameter keluaran ke fungsi unmanaged.
Sampel ini menunjukkan cara memanggil fungsi asli dengan menggunakan kelas Marshal dan dengan menggunakan kode yang tidak aman.
Sampel ini menggunakan fungsi pembungkus dan pemanggilan platform yang ditentukan dalam PinvokeLib.dll, yang disediakan dalam file sumber. Ini menggunakan fungsi TestOutArrayOfStructs
dan struktur MYSTRSTRUCT2
. Struktur berisi elemen-elemen berikut:
typedef struct _MYSTRSTRUCT2
{
char* buffer;
UINT size;
} MYSTRSTRUCT2;
Kelas MyStruct
berisi objek string karakter ANSI. Bidang CharSet menentukan format ANSI.
MyUnsafeStruct
, adalah struktur yang berisi tipe IntPtr bukan string.
Kelas NativeMethods
berisi metode prototipe TestOutArrayOfStructs
yang kelebihan beban. Jika metode mendeklarasikan pointer sebagai parameter, kelas harus ditandai dengan kata kunci unsafe
. Karena Visual Basic tidak dapat menggunakan kode yang tidak aman, metode kelebihan beban, pengubah yang tidak aman, dan struktur MyUnsafeStruct
tidak perlu.
Kelas App
mengimplementasikan metode UsingMarshaling
, yang melakukan semua tugas yang diperlukan untuk meneruskan array. Array ditandai dengan kata kunci out
(ByRef
di Visual Basic) untuk menunjukkan bahwa data diteruskan dari pihak yang menerima panggilan ke pemanggil. Implementasi menggunakan metode kelas Marshal berikut:
PtrToStructure memindahkan data dari buffer yang tidak dikelola ke objek yang dikelola.
DestroyStructure untuk melepaskan memori yang dialokasikan untuk string dalam struktur.
FreeCoTaskMem untuk membebaskan memori yang dicadangkan untuk array.
Seperti yang disebutkan sebelumnya, C# memungkinkan kode yang tidak aman dan Visual Basic tidak. Dalam sampel C#, UsingUnsafePointer
adalah implementasi metode alternatif yang menggunakan penunjuk alih-alih kelas Marshal untuk meneruskan kembali array yang berisi struktur MyUnsafeStruct
.
Mendeklarasikan Prototipe
// 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
Fungsi Panggilan
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
Lihat juga
- Marshalling Data dengan Platform Invoke
- Marshalling Strings
- Penyusunan Berbagai Jenis Array