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.
Marshalling adalah proses mengubah jenis ketika mereka perlu berpindah antara kode terkelola dan kode asli.
Marshalling diperlukan karena jenis dalam kode terkelola dan tidak terkelola berbeda. Dalam kode terkelola, misalnya, Anda mempunyai string, sementara string yang tidak dikelola dapat berupa pengodean .NET string (UTF-16), pengodean Halaman Kode ANSI, UTF-8, diakhiri dengan null, ASCII, dll. Secara default, subsistem P/Invoke berusaha melakukan hal yang tepat berdasarkan perilaku default, yang dijelaskan dalam artikel ini. Namun, untuk situasi di mana Anda memerlukan kontrol ekstra, Anda dapat menggunakan atribut MarshalAs untuk menentukan jenis yang diharapkan di sisi yang tidak dikelola. Misalnya, jika Anda ingin mengirim string sebagai string UTF-8 yang berakhir dengan null, Anda dapat melakukannya seperti ini:
[LibraryImport("somenativelibrary.dll")]
static extern int MethodA([MarshalAs(UnmanagedType.LPUTF8Str)] string parameter);
// or
[LibraryImport("somenativelibrary.dll", StringMarshalling = StringMarshalling.Utf8)]
static extern int MethodB(string parameter);
Jika Anda menerapkan System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute atribut ke assembly, aturan di bagian berikut tidak berlaku. Untuk informasi tentang bagaimana nilai .NET diekspos ke kode asli saat atribut ini diterapkan, lihat marshalling runtime yang dinonaktifkan.
Aturan default untuk pengaturan data jenis umum
Umumnya, waktu berjalan mencoba melakukan "hal yang benar" ketika melakukan marshalling agar Anda tidak perlu melakukan banyak pekerjaan. Tabel berikut menjelaskan bagaimana setiap jenis data diproses secara default saat digunakan dalam parameter atau kolom. Bilangan bulat dan jenis karakter lebar tetap C99/C++11 digunakan untuk memastikan bahwa tabel berikut ini benar untuk semua platform. Anda dapat menggunakan tipe asli apa pun yang memiliki persyaratan alignment dan ukuran yang sama dengan tipe ini.
Tabel pertama ini menjelaskan pemetaan untuk jenis di mana proses marshalling sama untuk P/Invoke dan marshalling bidang.
| Kata kunci C# | Jenis .NET | Jenis Asli |
|---|---|---|
byte |
System.Byte |
uint8_t |
sbyte |
System.SByte |
int8_t |
short |
System.Int16 |
int16_t |
ushort |
System.UInt16 |
uint16_t |
int |
System.Int32 |
int32_t |
uint |
System.UInt32 |
uint32_t |
long |
System.Int64 |
int64_t |
ulong |
System.UInt64 |
uint64_t |
char |
System.Char |
Baik char atau char16_t tergantung pada pengodean P/Invoke atau struktur. Lihat dokumentasi charset. |
System.Char |
Baik char* atau char16_t* tergantung pada pengodean P/Invoke atau struktur. Lihat dokumentasi pengkodean karakter. |
|
nint |
System.IntPtr |
intptr_t |
nuint |
System.UIntPtr |
uintptr_t |
Jenis Penunjuk .NET (mis. void*) |
void* |
|
Jenis berasal dari System.Runtime.InteropServices.SafeHandle |
void* |
|
Jenis berasal dari System.Runtime.InteropServices.CriticalHandle |
void* |
|
bool |
System.Boolean |
Jenis Win32 BOOL |
decimal |
System.Decimal |
Struktur COM DECIMAL |
| Delegasi .NET | Penunjuk fungsi asli | |
System.DateTime |
Jenis Win32 DATE |
|
System.Guid |
Jenis Win32 GUID |
Beberapa kategori marshalling memiliki default yang berbeda ketika Anda melakukan marshalling dalam bentuk parameter atau struktur.
| Jenis .NET | Jenis Asli (Parameter) | Jenis Asli (Bidang) |
|---|---|---|
| Array pada .NET | Penunjuk ke awal satu array yang berisi representasi-representasi asli dari elemen-elemen array. | Tidak diizinkan tanpa [MarshalAs] atribut |
Kelas dengan LayoutKind dari Sequential atau Explicit |
Penunjuk ke representasi asli kelas | Representasi alami kelas |
Tabel berikut ini menyertakan aturan marshalling default yang hanya untuk Windows. Pada platform non-Windows, Anda tidak dapat melakukan marshal jenis ini.
| Jenis .NET | Jenis Asli (Parameter) | Jenis Asli (Bidang) |
|---|---|---|
System.Object |
VARIANT |
IUnknown* |
System.Array |
Antarmuka COM | Tidak diizinkan tanpa [MarshalAs] atribut |
System.ArgIterator |
va_list |
Tidak diizinkan |
System.Collections.IEnumerator |
IEnumVARIANT* |
Tidak diizinkan |
System.Collections.IEnumerable |
IDispatch* |
Tidak diizinkan |
System.DateTimeOffset |
int64_t mewakili jumlah kutu sejak tengah malam pada 1 Januari 1601 |
int64_t mewakili jumlah kutu sejak tengah malam pada 1 Januari 1601 |
Beberapa jenis hanya dapat diubah sebagai parameter dan bukan sebagai kolom. Jenis-jenis ini tercantum dalam tabel berikut:
| Jenis .NET | Tipe Asli (Parameter Saja) |
|---|---|
System.Text.StringBuilder |
Baik char* atau char16_t* bergantung pada CharSet dari P/Invoke. Lihat dokumentasi charset. |
System.ArgIterator |
va_list (hanya pada Windows x86/x64/arm64) |
System.Runtime.InteropServices.ArrayWithOffset |
void* |
System.Runtime.InteropServices.HandleRef |
void* |
Jika pengaturan default ini tidak melakukan apa yang Anda inginkan, Anda dapat menyesuaikan cara parameter diproses. Artikel marshalling parameter memandu Anda tentang cara menyesuaikan bagaimana berbagai jenis parameter dimarshalling.
Pengaturan default marshalling dalam konteks COM
Ketika Anda memanggil metode pada objek COM di .NET, runtime .NET mengubah aturan marshalling default agar sesuai dengan semantik COM umum. Tabel berikut mencantumkan aturan yang digunakan runtime .NET dalam skenario COM:
| Jenis .NET | Jenis Asli (panggilan metode COM) |
|---|---|
System.Boolean |
VARIANT_BOOL |
StringBuilder |
LPWSTR |
System.String |
BSTR |
| Jenis Delegate |
_Delegate* di .NET Framework. Tidak diizinkan dalam .NET Core dan .NET 5+. |
System.Drawing.Color |
OLECOLOR |
| .NET array | SAFEARRAY |
System.String[] |
SAFEARRAY dari BSTR |
Marshalling kelas dan struktur data
Aspek lain dari jenis marshalling adalah cara meneruskan struktur ke metode yang tidak dikelola. Misalnya, beberapa metode yang tidak dikelola memerlukan struktur sebagai parameter. Dalam kasus ini, Anda perlu membuat struktur yang sesuai atau kelas di bagian terkelola dunia untuk menggunakannya sebagai parameter. Namun, hanya menentukan kelas tidak cukup, Anda juga perlu menginstruksikan marshaller cara memetakan bidang di kelas ke struktur yang tidak dikelola.
StructLayout Di sini atribut menjadi berguna.
using System;
using System.Runtime.InteropServices;
Win32Interop.GetSystemTime(out Win32Interop.SystemTime systemTime);
Console.WriteLine(systemTime.Year);
internal static partial class Win32Interop
{
[LibraryImport("kernel32.dll")]
internal static partial void GetSystemTime(out SystemTime systemTime);
[StructLayout(LayoutKind.Sequential)]
internal ref struct SystemTime
{
public ushort Year;
public ushort Month;
public ushort DayOfWeek;
public ushort Day;
public ushort Hour;
public ushort Minute;
public ushort Second;
public ushort Millisecond;
}
}
Kode sebelumnya menunjukkan contoh sederhana pemanggilan ke dalam GetSystemTime() fungsi. Bit yang menarik ada di baris 13. Atribut menentukan bahwa bidang kelas harus dipetakan secara berurutan ke struktur di sisi lain (tidak terkelola). Ini berarti bahwa penamaan bidang tidak penting, hanya urutannya yang penting, karena perlu sesuai dengan struktur yang tidak dikelola, yang ditunjukkan dalam contoh berikut:
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;
Terkadang pengaturan standar untuk marshalling struktur Anda tidak melakukan apa yang Anda butuhkan. Artikel Menyesuaikan struktur marshalling mengajarkan Anda cara menyesuaikan bagaimana struktur Anda diproses.