Pengaturan Default untuk String

System.String dan System.Text.StringBuilder memiliki perilaku marshalling yang sama.

String disusun sebagai jenis BSTR gaya COM atau sebagai string yang dihentikan null (array karakter yang diakhiri dengan karakter null). Karakter dalam string dapat disusun sebagai Unicode (default pada sistem Windows) atau ANSI.

String yang digunakan dalam antarmuka

Tabel berikut menunjukkan opsi marshalling untuk jenis data string ketika disusun sebagai argumen metode ke kode tak terkelola. Atribut MarshalAsAttribute menyediakan beberapa nilai enumerasi UnmanagedType untuk menyusun string ke antarmuka COM.

Jenis Enumerasi Deskripsi format yang tidak terkelola
UnmanagedType.BStr (default) BSTR bergaya COM dengan panjang prefiks dan karakter Unicode.
UnmanagedType.LPStr Penunjuk ke array karakter ANSI yang dihentikan null.
UnmanagedType.LPWStr Penunjuk ke array karakter Unicode yang dihentikan null.

Tabel ini berlaku untuk String. Untuk StringBuilder, satu-satunya opsi yang diizinkan adalah UnmanagedType.LPStr dan UnmanagedType.LPWStr.

Contoh berikut menunjukkan string yang dideklarasikan dalam antarmuka IStringWorker.

public interface IStringWorker
{
    void PassString1(string s);
    void PassString2([MarshalAs(UnmanagedType.BStr)] string s);
    void PassString3([MarshalAs(UnmanagedType.LPStr)] string s);
    void PassString4([MarshalAs(UnmanagedType.LPWStr)] string s);
    void PassStringRef1(ref string s);
    void PassStringRef2([MarshalAs(UnmanagedType.BStr)] ref string s);
    void PassStringRef3([MarshalAs(UnmanagedType.LPStr)] ref string s);
    void PassStringRef4([MarshalAs(UnmanagedType.LPWStr)] ref string s);
}
Public Interface IStringWorker
    Sub PassString1(s As String)
    Sub PassString2(<MarshalAs(UnmanagedType.BStr)> s As String)
    Sub PassString3(<MarshalAs(UnmanagedType.LPStr)> s As String)
    Sub PassString4(<MarshalAs(UnmanagedType.LPWStr)> s As String)
    Sub PassStringRef1(ByRef s As String)
    Sub PassStringRef2(<MarshalAs(UnmanagedType.BStr)> ByRef s As String)
    Sub PassStringRef3(<MarshalAs(UnmanagedType.LPStr)> ByRef s As String)
    Sub PassStringRef4(<MarshalAs(UnmanagedType.LPWStr)> ByRef s As String)
End Interface

Contoh berikut menunjukkan antarmuka yang sesuai yang dijelaskan dalam pustaka jenis.

interface IStringWorker : IDispatch
{
    HRESULT PassString1([in] BSTR s);
    HRESULT PassString2([in] BSTR s);
    HRESULT PassString3([in] LPStr s);
    HRESULT PassString4([in] LPWStr s);
    HRESULT PassStringRef1([in, out] BSTR *s);
    HRESULT PassStringRef2([in, out] BSTR *s);
    HRESULT PassStringRef3([in, out] LPStr *s);
    HRESULT PassStringRef4([in, out] LPWStr *s);
};

String yang digunakan dalam pemanggilan platform

Ketika CharSet adalah Unicode atau argumen string secara eksplisit ditandai sebagai [MarshalAs(UnmanagedType.LPWSTR)] dan string diteruskan dengan nilai (bukan ref atau out), string akan disematkan dan digunakan langsung oleh kode asli. Jika tidak, platform memanggil salinan argumen string, mengkonversi dari format .NET Framework (Unicode) ke format platform yang tidak terkelola. String tidak dapat diubah dan tidak disalin kembali dari memori tidak terkelola ke memori terkelola saat panggilan kembali.

Kode asli hanya bertanggung jawab untuk merilis memori ketika string diteruskan oleh referensi dan menerapkan nilai baru. Jika tidak, runtime .NET memiliki memori dan akan merilisnya setelah panggilan.

Tabel berikut mencantumkan opsi marshalling untuk string saat diurut sebagai argumen metode dari panggilan pemanggilan platform. Atribut MarshalAsAttribute menyediakan beberapa nilai enumerasi UnmanagedType untuk menyusun string.

Jenis Enumerasi Deskripsi format yang tidak terkelola
UnmanagedType.AnsiBStr BSTR bergaya COM dengan panjang prefiks dan karakter ANSI.
UnmanagedType.BStr BSTR bergaya COM dengan panjang prefiks dan karakter Unicode.
UnmanagedType.LPStr (default) Penunjuk ke array karakter ANSI yang dihentikan null.
UnmanagedType.LPTStr Penunjuk ke array karakter yang bergantung pada platform yang dihentikan null.
UnmanagedType.LPUTF8Str Penunjuk ke array karakter yang dikodekan UTF-8 yang dihentikan null.
UnmanagedType.LPWStr Penunjuk ke array karakter Unicode yang dihentikan null.
UnmanagedType.TBStr BSTR gaya COM dengan panjang prefiks dan karakter yang bergantung pada platform.
VBByRefStr Nilai yang memungkinkan Visual Basic mengubah string dalam kode tak terkelola dan memiliki hasil yang tercermin dalam kode terkelola. Nilai ini hanya didukung untuk pemanggilan platform. Ini adalah nilai default dalam Visual Basic untuk string ByVal.

Tabel ini berlaku untuk String. Untuk StringBuilder, satu-satunya opsi yang diizinkan adalah LPStr, LPTStr, dan LPWStr.

Definisi jenis berikut menunjukkan penggunaan MarshalAsAttribute yang benar untuk panggilan pemanggilan platform.

class StringLibAPI
{
    [DllImport("StringLib.dll")]
    public static extern void PassLPStr([MarshalAs(UnmanagedType.LPStr)] string s);
    [DllImport("StringLib.dll")]
    public static extern void PassLPWStr([MarshalAs(UnmanagedType.LPWStr)] string s);
    [DllImport("StringLib.dll")]
    public static extern void PassLPTStr([MarshalAs(UnmanagedType.LPTStr)] string s);
    [DllImport("StringLib.dll")]
    public static extern void PassLPUTF8Str([MarshalAs(UnmanagedType.LPUTF8Str)] string s);
    [DllImport("StringLib.dll")]
    public static extern void PassBStr([MarshalAs(UnmanagedType.BStr)] string s);
    [DllImport("StringLib.dll")]
    public static extern void PassAnsiBStr([MarshalAs(UnmanagedType.AnsiBStr)] string s);
    [DllImport("StringLib.dll")]
    public static extern void PassTBStr([MarshalAs(UnmanagedType.TBStr)] string s);
}
Class StringLibAPI
    Public Declare Auto Sub PassLPStr Lib "StringLib.dll" (
        <MarshalAs(UnmanagedType.LPStr)> s As String)
    Public Declare Auto Sub PassLPWStr Lib "StringLib.dll" (
        <MarshalAs(UnmanagedType.LPWStr)> s As String)
    Public Declare Auto Sub PassLPTStr Lib "StringLib.dll" (
        <MarshalAs(UnmanagedType.LPTStr)> s As String)
    Public Declare Auto Sub PassLPUTF8Str Lib "StringLib.dll" (
        <MarshalAs(UnmanagedType.LPUTF8Str)> s As String)
    Public Declare Auto Sub PassBStr Lib "StringLib.dll" (
        <MarshalAs(UnmanagedType.BStr)> s As String)
    Public Declare Auto Sub PassAnsiBStr Lib "StringLib.dll" (
        <MarshalAs(UnmanagedType.AnsiBStr)> s As String)
    Public Declare Auto Sub PassTBStr Lib "StringLib.dll" (
        <MarshalAs(UnmanagedType.TBStr)> s As String)
End Class

String yang digunakan dalam struktur

String adalah anggota struktur yang valid; namun, StringBuilder buffer tidak valid dalam struktur. Tabel berikut ini menunjukkan opsi marshalling untuk jenis data String saat jenis disusun sebagai bidang. Atribut MarshalAsAttribute menyediakan beberapa nilai enumerasi UnmanagedType untuk menyusun string ke bidang.

Jenis Enumerasi Deskripsi format yang tidak terkelola
UnmanagedType.BStr BSTR bergaya COM dengan panjang prefiks dan karakter Unicode.
UnmanagedType.LPStr (default) Penunjuk ke array karakter ANSI yang dihentikan null.
UnmanagedType.LPTStr Penunjuk ke array karakter yang bergantung pada platform yang dihentikan null.
UnmanagedType.LPUTF8Str Penunjuk ke array karakter yang dikodekan UTF-8 yang dihentikan null.
UnmanagedType.LPWStr Penunjuk ke array karakter Unicode yang dihentikan null.
UnmanagedType.ByValTStr Array karakter dengan panjang tetap; jenis array ditentukan oleh tataan karakter dari struktur yang dikandungnya.

Jenis ByValTStr digunakan untuk array karakter sebaris dengan panjang tetap yang muncul dalam struktur. Jenis lain berlaku untuk referensi string yang terkandung dalam struktur yang berisi pointer ke string.

Argumen CharSet dari StructLayoutAttribute yang diterapkan ke struktur yang terkandung menentukan format karakter string dalam struktur. Contoh struktur berikut berisi referensi string dan string sebaris serta karakter ANSI, Unicode, dan tergantung platform. Representasi struktur ini dalam pustaka jenis ditampilkan dalam kode C ++ berikut:

struct StringInfoA
{
    char *  f1;
    char    f2[256];
};

struct StringInfoW
{
    WCHAR * f1;
    WCHAR   f2[256];
    BSTR    f3;
};

struct StringInfoT
{
    TCHAR * f1;
    TCHAR   f2[256];
};

Contoh berikut menunjukkan cara menggunakan MarshalAsAttribute untuk menentukan struktur yang sama dalam format yang berbeda.

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct StringInfoA
{
    [MarshalAs(UnmanagedType.LPStr)] public string f1;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string f2;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct StringInfoW
{
    [MarshalAs(UnmanagedType.LPWStr)] public string f1;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string f2;
    [MarshalAs(UnmanagedType.BStr)] public string f3;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct StringInfoT
{
    [MarshalAs(UnmanagedType.LPTStr)] public string f1;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] public string f2;
}
<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Ansi)> _
Structure StringInfoA
    <MarshalAs(UnmanagedType.LPStr)> Public f1 As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
    Public f2 As String
End Structure

<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Unicode)> _
Structure StringInfoW
    <MarshalAs(UnmanagedType.LPWStr)> Public f1 As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
    Public f2 As String
<MarshalAs(UnmanagedType.BStr)> Public f3 As String
End Structure

<StructLayout(LayoutKind.Sequential, CharSet := CharSet.Auto)> _
Structure StringInfoT
    <MarshalAs(UnmanagedType.LPTStr)> Public f1 As String
    <MarshalAs(UnmanagedType.ByValTStr, SizeConst := 256)> _
    Public f2 As String
End Structure

Buffer string dengan panjang tetap

Dalam beberapa keadaan, buffer karakter dengan panjang tetap harus diteruskan ke dalam kode tak terkelola untuk dimanipulasi. Meneruskan string saja tidak berfungsi dalam kasus ini karena penerima panggilan tidak dapat mengubah konten buffer yang diteruskan. Bahkan jika string diteruskan dengan referensi, tidak ada cara untuk menginisialisasi buffer ke ukuran tertentu.

Solusinya adalah dengan meneruskan byte[] atau char[], tergantung pada pengodean yang diharapkan, sebagai argumen alih-alih String. Array, ketika ditandai dengan [Out], dapat didereferensikan dan diubah oleh penerima panggilan, asalkan tidak melebihi kapasitas array yang dialokasikan.

Misalnya, fungsi API Windows GetWindowText (ditentukan dalam winuser.h) mengharuskan penelepon meneruskan buffer dengan karakter panjang tetap ke tempat fungsi menulis teks jendela. Argumen lpString menunjuk ke buffer ukuran nMaxCountyang dialokasikan pemanggil. Pemanggil diharapkan mengalokasikan buffer dan mengatur argumen nMaxCount ke ukuran buffer yang dialokasikan. Contoh berikut menunjukkan deklarasi fungsi GetWindowText seperti yang ditentukan dalam winuser.h.

int GetWindowText(
    HWND hWnd,        // Handle to window or control.
    LPTStr lpString,  // Text buffer.
    int nMaxCount     // Maximum number of characters to copy.
);

char[] dapat didereferensikan dan diubah oleh penerima panggilan. Contoh kode berikut menunjukkan bagaimana ArrayPool<char> dapat digunakan untuk mengalokasikan char[] sebelumnya.

using System;
using System.Buffers;
using System.Runtime.InteropServices;

internal static class NativeMethods
{
    [DllImport("User32.dll", CharSet = CharSet.Unicode)]
    public static extern void GetWindowText(IntPtr hWnd, [Out] char[] lpString, int nMaxCount);
}

public class Window
{
    internal IntPtr h;        // Internal handle to Window.
    public string GetText()
    {
        char[] buffer = ArrayPool<char>.Shared.Rent(256 + 1);
        NativeMethods.GetWindowText(h, buffer, buffer.Length);
        return new string(buffer);
    }
}
Imports System
Imports System.Buffers
Imports System.Runtime.InteropServices

Friend Class NativeMethods
    Public Declare Auto Sub GetWindowText Lib "User32.dll" _
        (hWnd As IntPtr, <Out> lpString() As Char, nMaxCount As Integer)
End Class

Public Class Window
    Friend h As IntPtr ' Friend handle to Window.
    Public Function GetText() As String
        Dim buffer() As Char = ArrayPool(Of Char).Shared.Rent(256 + 1)
        NativeMethods.GetWindowText(h, buffer, buffer.Length)
        Return New String(buffer)
   End Function
End Class

Solusi lain adalah meneruskan StringBuilder sebagai argumen alih-alih String. Buffer yang dibuat saat mengurutkan StringBuilder dapat didereferensikan dan diubah oleh penerima panggilan, asalkan tidak melebihi kapasitas StringBuilder. Ini juga dapat diinsialisasi dengan panjang tetap. Misalnya, jika Anda menginisialisasi buffer StringBuilder ke kapasitas N, pengurut menyediakan karakter buffer dengan ukuran (N+1). +1 menjelaskan fakta bahwa string yang tidak terkelola memiliki pembatas null sementara StringBuilder tidak.

Catatan

Secara umum, meneruskan argumen StringBuilder tidak disarankan jika Anda mengkhawatirkan performa. Untuk informasi selengkapnya, lihat Parameter string.

Lihat juga