Bagikan melalui


Pengaturan default untuk pengelolaan string

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

String diproses sebagai COM-style BSTR atau sebagai string yang diakhiri dengan null (array karakter yang diakhiri dengan null). Karakter dalam string dapat dinamai sebagai Unicode (default pada sistem Windows) atau ANSI.

String yang digunakan dalam antarmuka

Tabel berikut memperlihatkan opsi marshalling untuk tipe data string saat dimarshall sebagai argumen metode ke kode yang tidak dikelola. Atribut MarshalAsAttribute menyediakan beberapa UnmanagedType nilai enumerasi untuk memproses string ke antarmuka COM.

Jenis enumerasi Deskripsi format tidak terkelola
UnmanagedType.BStr (standar) Gaya COM BSTR dengan panjang yang sudah ditentukan dan karakter Unicode.
UnmanagedType.LPStr Penunjuk ke array karakter ANSI yang diakhiri dengan karakter null otomatis.
UnmanagedType.LPWStr Penunjuk ke array karakter Unicode yang diakhiri dengan 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 IStringWorker antarmuka.

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 tipe.

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 pada platform

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

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

Tabel berikut mencantumkan opsi dimarshalling untuk string saat dimarshall sebagai argumen fungsi dari pemanggilan API platform. Atribut MarshalAsAttribute ini menyediakan beberapa UnmanagedType nilai enumerasi untuk pengolahan string.

Jenis enumerasi Deskripsi format tidak terkelola
UnmanagedType.AnsiBStr Gaya BSTR COM dengan panjang awalan dan karakter ANSI.
UnmanagedType.BStr Gaya COM BSTR dengan panjang yang sudah ditentukan dan karakter Unicode.
UnmanagedType.LPStr (standar) Penunjuk ke array karakter ANSI yang diakhiri dengan karakter null otomatis.
UnmanagedType.LPTStr Penunjuk ke array karakter yang tergantung pada platform dan diakhiri dengan null.
UnmanagedType.LPUTF8Str Penunjuk ke array karakter berkode UTF-8 yang dihentikan null.
UnmanagedType.LPWStr Penunjuk ke array karakter Unicode yang diakhiri dengan null.
UnmanagedType.TBStr Gaya BSTR COM dengan panjang awalan dan karakter tergantung platform.
VBByRefStr Nilai yang memungkinkan Visual Basic mengubah string dalam kode yang tidak dikelola dan memiliki hasil yang tercermin dalam kode terkelola. Nilai ini hanya didukung dalam konteks pemanggilan fungsi platform. Ini adalah nilai default di Visual Basic untuk ByVal string.

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

Definisi tipe berikut menunjukkan penggunaan MarshalAsAttribute yang benar untuk panggilan platform invoke.

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 menunjukkan opsi marshalling untuk tipe data String ketika tipe tersebut dimarshall sebagai bidang. Atribut MarshalAsAttribute menyediakan beberapa nilai enumerasi UnmanagedType untuk memindahkan string ke sebuah bidang.

Jenis enumerasi Deskripsi format tidak terkelola
UnmanagedType.BStr Gaya COM BSTR dengan panjang yang sudah ditentukan dan karakter Unicode.
UnmanagedType.LPStr (standar) Penunjuk ke array karakter ANSI yang diakhiri dengan karakter null otomatis.
UnmanagedType.LPTStr Penunjuk ke array karakter yang tergantung pada platform dan diakhiri dengan null.
UnmanagedType.LPUTF8Str Penunjuk ke array karakter berkode UTF-8 yang dihentikan null.
UnmanagedType.LPWStr Penunjuk ke array karakter Unicode yang diakhiri dengan null.
UnmanagedType.ByValTStr Array karakter dengan panjang tetap; jenis array ditentukan oleh kumpulan karakter dari struktur yang berisi.

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

Argumen CharSetStructLayoutAttribute yang diterapkan ke struktur yang berisi menentukan format karakter string dalam struktur. Struktur contoh berikut ini berisi referensi string dan string sebaris, serta karakter ANSI, Unicode, dan karakter yang bergantung pada platform. Representasi struktur ini dalam pustaka jenis diperlihatkan 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 panjang tetap harus diteruskan ke dalam kode yang tidak dikelola untuk dimanipulasi. Hanya meneruskan string tidak berfungsi dalam kasus ini karena penerima panggilan tidak dapat memodifikasi konten buffer yang dilewatkan. Bahkan jika string diteruskan oleh referensi, tidak ada cara untuk menginisialisasi buffer ke ukuran tertentu.

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

Misalnya, fungsi WINDOWS GetWindowText API (didefinisikan dalam winuser.h) mengharuskan penelepon meneruskan buffer karakter panjang tetap tempat fungsi menulis teks jendela. Argumen lpString menunjuk ke buffer ukuran nMaxCountyang dialokasikan pemanggil. Pemanggil diharapkan mengalokasikan buffer dan mengatur nMaxCount argumen ke ukuran buffer yang dialokasikan. Contoh berikut menunjukkan deklarasi fungsi seperti yang GetWindowText didefinisikan 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 dimodifikasi 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 ketika marshalling StringBuilder dapat didereferensikan dan dimodifikasi oleh penerima panggilan, asalkan tidak melebihi kapasitas StringBuilder. Ini juga dapat diinisialisasi ke panjang tetap. Misalnya, jika Anda menginisialisasi buffer StringBuilder dengan kapasitas N, marshaller akan menyediakan buffer dengan ukuran (N+1) karakter. +1 memperhitungkan fakta bahwa string yang tidak dikelola memiliki penanda akhir null sedangkan StringBuilder tidak.

Nota

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

Lihat juga