Výchozí zařazování řetězců

Obě třídy System.StringSystem.Text.StringBuilder mají podobné zařazování chování.

Řetězce jsou zařazovány jako typ stylu BSTR MODELU COM nebo jako řetězec ukončený hodnotou null (pole znaků končící znakem null). Znaky v řetězci mohou být zařazovány jako Unicode (výchozí nastavení v systémech Windows) nebo ANSI.

Řetězce používané v rozhraních

Následující tabulka ukazuje možnosti zařazování pro datový typ řetězce při zařazování jako argument metody pro nespravovaný kód. Atribut MarshalAsAttribute poskytuje několik UnmanagedType hodnot výčtu pro zařazování řetězců do rozhraní MODELU COM.

Typ výčtu Popis nespravovaného formátu
UnmanagedType.BStr (výchozí) Styl BSTR modelu COM s předponou délky a znaky Unicode.
UnmanagedType.LPStr Ukazatel na pole s ukončenou hodnotou null znaků ANSI.
UnmanagedType.LPWStr Ukazatel na pole znaků Unicode s ukončenou hodnotou null.

Tato tabulka platí pro String. Pro StringBuilder, jediné povolené možnosti jsou UnmanagedType.LPStr a UnmanagedType.LPWStr.

Následující příklad ukazuje řetězce deklarované v IStringWorker rozhraní.

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

Následující příklad ukazuje odpovídající rozhraní popsané v knihovně typů.

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);
};

Řetězce používané při volání platformy

Pokud je CharSet Unicode nebo řetězcový argument explicitně označen jako [MarshalAs(UnmanagedType.LPWSTR)] a řetězec se předá hodnotou (ne ref nebo out), řetězec se připne a použije přímo nativním kódem. V opačném případě platforma vyvolá kopie řetězcové argumenty, převede z formátu .NET Framework (Unicode) na nespravovaný formát platformy. Řetězce jsou neměnné a při vrácení volání se nekopírují zpět z nespravované paměti do spravované paměti.

Nativní kód je zodpovědný pouze za uvolnění paměti, když je řetězec předán odkazem a přiřadí novou hodnotu. V opačném případě modul runtime .NET vlastní paměť a uvolní ji po volání.

V následující tabulce jsou uvedeny možnosti zařazování řetězců při zařazování jako argument metody volání vyvolání platformy. Atribut MarshalAsAttribute poskytuje několik UnmanagedType hodnot výčtu pro zařazování řetězců.

Typ výčtu Popis nespravovaného formátu
UnmanagedType.AnsiBStr Styl BSTR modelu COM s předponou a znaky ANSI
UnmanagedType.BStr Styl BSTR modelu COM s předponou délky a znaky Unicode.
UnmanagedType.LPStr (výchozí) Ukazatel na pole s ukončenou hodnotou null znaků ANSI.
UnmanagedType.LPTStr Ukazatel na pole znaky závislé na platformě s hodnotou null.
UnmanagedType.LPUTF8Str Ukazatel na pole s kódováním UTF-8 s hodnotou null.
UnmanagedType.LPWStr Ukazatel na pole znaků Unicode s ukončenou hodnotou null.
UnmanagedType.TBStr Styl BSTR modelu COM s předponou délky a znaky závislými na platformě.
VBByRefStr Hodnota, která umožňuje jazyku Visual Basic změnit řetězec v nespravovaném kódu a výsledky se projeví ve spravovaném kódu. Tato hodnota je podporována pouze pro volání platformy. Toto je výchozí hodnota jazyka Visual Basic pro ByVal řetězce.

Tato tabulka platí pro String. Pro StringBuilder, jediné povolené možnosti jsou LPStr, LPTStra LPWStr.

Následující definice typu ukazuje správné použití MarshalAsAttribute volání volání volání platformy.

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

Řetězce používané ve strukturách

Řetězce jsou platnými členy struktur; StringBuilder vyrovnávací paměti jsou však ve strukturách neplatné. Následující tabulka ukazuje možnosti seřazování datového String typu, když je typ zařazován jako pole. Atribut MarshalAsAttribute poskytuje několik UnmanagedType hodnot výčtu pro zařazování řetězců do pole.

Typ výčtu Popis nespravovaného formátu
UnmanagedType.BStr Styl BSTR modelu COM s předponou délky a znaky Unicode.
UnmanagedType.LPStr (výchozí) Ukazatel na pole s ukončenou hodnotou null znaků ANSI.
UnmanagedType.LPTStr Ukazatel na pole znaky závislé na platformě s hodnotou null.
UnmanagedType.LPUTF8Str Ukazatel na pole s kódováním UTF-8 s hodnotou null.
UnmanagedType.LPWStr Ukazatel na pole znaků Unicode s ukončenou hodnotou null.
UnmanagedType.ByValTStr Pole znaků s pevnou délkou; typ pole je určen znakovou sadou obsahující struktury.

Typ ByValTStr se používá pro vložená pole znaků s pevnou délkou, která se zobrazují ve struktuře. Jiné typy se vztahují na odkazy na řetězce obsažené ve strukturách, které obsahují ukazatele na řetězce.

Argument CharSet použitého StructLayoutAttribute u struktury obsahující určuje formát znaků řetězců ve strukturách. Následující ukázkové struktury obsahují odkazy na řetězce a vložené řetězce a také znaky závislé na standardu ANSI, Unicode a platformě. Reprezentace těchto struktur v knihovně typů je znázorněna v následujícím kódu jazyka C++:

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

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

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

Následující příklad ukazuje, jak použít MarshalAsAttribute k definování stejné struktury v různých formátech.

[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

Vyrovnávací paměti řetězců s pevnou délkou

V některých případech musí být vyrovnávací paměť znaků s pevnou délkou předána do nespravovaného kódu, který se má manipulovat. Jednoduše předání řetězce v tomto případě nefunguje, protože volaný nemůže změnit obsah předané vyrovnávací paměti. I když je řetězec předán odkazem, neexistuje způsob, jak inicializovat vyrovnávací paměť na danou velikost.

Řešením je předat nebo byte[]char[], v závislosti na očekávaném kódování, jako argument místo argumentu String. Pole, pokud je označeno značkou [Out], může být dereferenced a změněno volaným, za předpokladu, že nepřekračuje kapacitu přiděleného pole.

Například funkce rozhraní API systému Windows GetWindowText (definovaná v winuser.h) vyžaduje, aby volající předal vyrovnávací paměť znaků s pevnou délkou, do které funkce zapisuje text okna. Argument lpString odkazuje na vyrovnávací paměť přidělenou volajícím nMaxCount. Očekává se, že volající přidělí vyrovnávací paměť a nastaví nMaxCount argument na velikost přidělené vyrovnávací paměti. Následující příklad ukazuje deklaraci funkce definovanou GetWindowText v winuser.h.

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

A char[] může být dereferenced a upraven volanou. Následující příklad kódu ukazuje, jak ArrayPool<char> lze použít k předběžnému přidělení char[].

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

Dalším řešením je předat StringBuilder jako argument místo argumentu String. Vyrovnávací paměť vytvořená při zařazování StringBuilder lze dereferenced a upravit volanou, pokud nepřekračuje kapacitu StringBuilder. Dá se také inicializovat na pevnou délku. Pokud například inicializujete StringBuilder vyrovnávací paměť do kapacity N, marshaller poskytuje vyrovnávací paměť s velikostí (N+1) znaků. +1 představuje skutečnost, že nespravovaný řetězec má ukončovací znak null, zatímco StringBuilder ne.

Poznámka:

Obecně platí, že předávání StringBuilder argumentů se nedoporučuje, pokud máte obavy o výkon. Další informace naleznete v tématu Parametry řetězce.

Viz také