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 memiliki string, sementara string yang tidak dikelola dapat menggunakan pengodean .NET string (UTF-16), pengodean Halaman Kode ANSI, UTF-8, berakhir dengan null, ASCII, dll. Secara default, subsistem P/Invoke mencoba beroperasi sesuai perilaku default, dijelaskan dalam artikel. 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 disabled runtime marshalling.
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.
Penting
Saat memanggil fungsi C yang menggunakan long, gunakan CLong atau CULong (.NET 6+) alih-alih C# long. Untuk detail dan penanganan sementara untuk versi .NET sebelumnya, lihat Pertimbangan jenis data lintas platform.
Nota
Jenis wchar_t adalah UTF-16 (2 byte) pada Windows tetapi ditentukan pengkompilasi pada platform lain—biasanya UTF-32 (4 byte) di Linux dan macOS. Karena itu, wchar_t* sulit digunakan sebagai ABI lintas platform tunggal. Saat Anda merancang API asli lintas platform, lebih suka char* dengan kontrak pengodean yang ditentukan dengan jelas (misalnya, UTF-8) alih-alih wchar_t*.
Nota
String asli char* menggunakan pengodean yang ditentukan pustaka atau platform. Saat Anda memanggil fungsi C yang mengambil char*, cocokkan pengodean yang diharapkan dengan memilih opsi marshalling string yang benar, seperti StringMarshalling.Utf8 untuk UTF-8, StringMarshalling.Utf16 untuk UTF-16, atau StringMarshalling.Custom untuk pengodean lainnya.
| 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 charset. |
|
nint |
System.IntPtr |
intptr_t |
nuint |
System.UIntPtr |
uintptr_t |
jenis .NET Pointer (misalnya 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 .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 khusus untuk Windows. Pada platform non-Windows, Anda tidak dapat memproses 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
Saat 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 oleh .NET runtime dalam skenario COM:
| Jenis .NET | Jenis Asli (panggilan metode COM) |
|---|---|
System.Boolean |
VARIANT_BOOL |
StringBuilder |
LPWSTR |
System.String |
BSTR |
| Jenis Delegate |
_Delegate* dalam .NET Framework. Dilarang 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.