Delen via


Standaard marshalling voor tekenreeksen

Zowel de als System.Text.StringBuilder de System.String klassen hebben vergelijkbaar marshallgedrag.

Tekenreeksen worden aangeduid als een COM-stijltype BSTR of als een tekenreeks met null-einden (een tekenmatrix die eindigt op een null-teken). De tekens in de tekenreeks kunnen worden ge marshalld als Unicode (de standaardinstelling op Windows-systemen) of ANSI.

Tekenreeksen die worden gebruikt in interfaces

In de volgende tabel ziet u de marshallopties voor het gegevenstype tekenreeks wanneer marshalling wordt gebruikt als een methodeargument voor onbeheerde code. Het MarshalAsAttribute kenmerk biedt verschillende UnmanagedType opsommingswaarden voor marshal strings naar COM-interfaces.

Opsommingstype Beschrijving van niet-beheerde indeling
UnmanagedType.BStr (standaard) Een COM-stijl BSTR met een voorvoegsellengte en Unicode-tekens.
UnmanagedType.LPStr Een aanwijzer naar een null-beëindigde matrix met ANSI-tekens.
UnmanagedType.LPWStr Een aanwijzer naar een null-beëindigde matrix met Unicode-tekens.

Deze tabel is van toepassing op String. Voor StringBuilder, de enige opties toegestaan zijn UnmanagedType.LPStr en UnmanagedType.LPWStr.

In het volgende voorbeeld ziet u tekenreeksen die zijn gedeclareerd in de IStringWorker interface.

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

In het volgende voorbeeld ziet u de bijbehorende interface die wordt beschreven in een typebibliotheek.

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

Tekenreeksen die worden gebruikt in het aanroepen van het platform

Wanneer de CharSet Unicode is of een tekenreeksargument expliciet is gemarkeerd als [MarshalAs(UnmanagedType.LPWSTR)] en de tekenreeks wordt doorgegeven door waarde (niet ref of out), wordt de tekenreeks vastgemaakt en rechtstreeks door systeemeigen code gebruikt. Anders roept het platform tekenreeksargumenten aan, die worden geconverteerd van de .NET Framework-indeling (Unicode) naar de niet-beheerde platformindeling. Tekenreeksen zijn onveranderbaar en worden niet gekopieerd van onbeheerd geheugen naar beheerd geheugen wanneer de aanroep wordt geretourneerd.

Systeemeigen code is alleen verantwoordelijk voor het vrijgeven van het geheugen wanneer de tekenreeks doorverwijzing wordt doorgegeven en er een nieuwe waarde aan wordt toegewezen. Anders is de .NET-runtime eigenaar van het geheugen en wordt deze na de aanroep vrijgegeven.

De volgende tabel bevat de marshallopties voor tekenreeksen wanneer marshalling wordt gebruikt als een methodeargument van een aanroep van een platform. Het MarshalAsAttribute kenmerk biedt verschillende UnmanagedType opsommingswaarden voor marshal strings.

Opsommingstype Beschrijving van niet-beheerde indeling
UnmanagedType.AnsiBStr Een COM-stijl BSTR met een voorvoegsellengte en ANSI-tekens.
UnmanagedType.BStr Een COM-stijl BSTR met een voorvoegsellengte en Unicode-tekens.
UnmanagedType.LPStr (standaard) Een aanwijzer naar een null-beëindigde matrix met ANSI-tekens.
UnmanagedType.LPTStr Een aanwijzer naar een door null beëindigde matrix met platformafhankelijke tekens.
UnmanagedType.LPUTF8Str Een aanwijzer naar een door null beëindigde matrix met met UTF-8 gecodeerde tekens.
UnmanagedType.LPWStr Een aanwijzer naar een null-beëindigde matrix met Unicode-tekens.
UnmanagedType.TBStr Een COM-stijl BSTR met een voorvoegsellengte en platformafhankelijke tekens.
VBByRefStr Een waarde waarmee Visual Basic een tekenreeks in niet-beheerde code kan wijzigen en de resultaten in beheerde code laat zien. Deze waarde wordt alleen ondersteund voor het aanroepen van het platform. Dit is de standaardwaarde in Visual Basic voor ByVal tekenreeksen.

Deze tabel is van toepassing op String. Voor StringBuilder, de enige opties zijn , LPStren LPTStrLPWStr.

In de volgende typedefinitie ziet u het juiste gebruik voor MarshalAsAttribute aanroepen van platformen.

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

Tekenreeksen die worden gebruikt in structuren

Tekenreeksen zijn geldige leden van structuren; StringBuilder buffers zijn echter ongeldig in structuren. In de volgende tabel ziet u de marshallopties voor het String gegevenstype wanneer het type als veld wordt ge marshalld. Het MarshalAsAttribute kenmerk biedt verschillende UnmanagedType opsommingswaarden voor marshal strings naar een veld.

Opsommingstype Beschrijving van niet-beheerde indeling
UnmanagedType.BStr Een COM-stijl BSTR met een voorvoegsellengte en Unicode-tekens.
UnmanagedType.LPStr (standaard) Een aanwijzer naar een null-beëindigde matrix met ANSI-tekens.
UnmanagedType.LPTStr Een aanwijzer naar een door null beëindigde matrix met platformafhankelijke tekens.
UnmanagedType.LPUTF8Str Een aanwijzer naar een door null beëindigde matrix met met UTF-8 gecodeerde tekens.
UnmanagedType.LPWStr Een aanwijzer naar een null-beëindigde matrix met Unicode-tekens.
UnmanagedType.ByValTStr Een matrix met vaste lengte van tekens; het type van de matrix wordt bepaald door de tekenset van de structuur die de matrix bevat.

Het ByValTStr type wordt gebruikt voor inline tekenmatrices met een vaste lengte die in een structuur worden weergegeven. Andere typen zijn van toepassing op tekenreeksverwijzingen in structuren die verwijzingen naar tekenreeksen bevatten.

Het CharSet argument van de StructLayoutAttribute structuur die wordt toegepast op de structuur bepaalt de tekenopmaak van tekenreeksen in structuren. De volgende voorbeeldstructuren bevatten tekenreeksverwijzingen en inlinetekenreeksen, evenals ANSI-, Unicode- en platformafhankelijke tekens. De weergave van deze structuren in een typebibliotheek wordt weergegeven in de volgende C++-code:

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

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

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

In het volgende voorbeeld ziet u hoe u de MarshalAsAttribute structuur in verschillende indelingen kunt definiëren.

[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

Tekenreeksbuffers met vaste lengte

In sommige gevallen moet een tekenbuffer met een vaste lengte worden doorgegeven aan onbeheerde code die moet worden gemanipuleerd. Het doorgeven van een tekenreeks werkt in dit geval niet omdat de gebruiker de inhoud van de doorgegeven buffer niet kan wijzigen. Zelfs als de tekenreeks wordt doorgegeven via verwijzing, is er geen manier om de buffer te initialiseren naar een bepaalde grootte.

De oplossing is het doorgeven van een byte[] of char[], afhankelijk van de verwachte codering, als het argument in plaats van een String. De matrix, indien gemarkeerd met [Out], kan worden gededucteerd en gewijzigd door de aangeroepene, mits deze niet groter is dan de capaciteit van de toegewezen matrix.

De Windows GetWindowText API-functie (gedefinieerd in winuser.h) vereist bijvoorbeeld dat de aanroeper een tekenbuffer met een vaste lengte doorgeeft waaraan de functie de tekst van het venster schrijft. Het lpString argument verwijst naar een aanroeper toegewezen buffer van grootte nMaxCount. De aanroeper wordt verwacht de buffer toe te wijzen en het nMaxCount argument in te stellen op de grootte van de toegewezen buffer. In het volgende voorbeeld ziet u de GetWindowText functiedeclaratie zoals gedefinieerd in winuser.h.

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

Een char[] kan worden gededucteerd en gewijzigd door de geroepen. In het volgende codevoorbeeld ziet u hoe ArrayPool<char> u een 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

Een andere oplossing is om een StringBuilder als argument door te geven in plaats van een String. De buffer die wordt gemaakt bij het marshallen van een StringBuilder kan worden gededucteerd en gewijzigd door de aangeroepene, mits deze niet groter is dan de capaciteit van de StringBuilder. Het kan ook worden geïnitialiseerd tot een vaste lengte. Als u bijvoorbeeld een StringBuilder buffer initialiseert naar een capaciteit van N, biedt de marshaller een buffer van grootte (N+1). De +1 houdt rekening met het feit dat de niet-beheerde tekenreeks een null-eindteken heeft, terwijl StringBuilder dat niet het geval is.

Notitie

Over het algemeen wordt het doorgeven StringBuilder van argumenten niet aanbevolen als u zich zorgen maakt over prestaties. Zie Tekenreeksparameters voor meer informatie.

Zie ook