Teilen über


Standard-Marshalling für Zeichenfolgen

Sowohl die Klassen als System.Text.StringBuilder auch die System.String Klassen weisen ein ähnliches Marshallverhalten auf.

Zeichenfolgen werden als COM-Typ BSTR oder als null-gekündigte Zeichenfolge (ein Zeichenarray, das mit einem Nullzeichen endet) ge marshallt. Die Zeichen innerhalb der Zeichenfolge können als Unicode (standard unter Windows-Systemen) oder ANSI ge marshallt werden.

In Schnittstellen verwendete Zeichenfolgen

In der folgenden Tabelle sind die Marshalloptionen für den Zeichenfolgendatentyp aufgeführt, wenn sie als Methodenargument für nicht verwalteten Code ge marshallt werden. Das MarshalAsAttribute Attribut stellt mehrere UnmanagedType Enumerationswerte zum Marshallen von Zeichenfolgen für COM-Schnittstellen bereit.

Enumerationstyp Beschreibung des nicht verwalteten Formats
UnmanagedType.BStr (Standardwert) Eine COM-Formatvorlage BSTR mit einer präfixierten Länge und Unicode-Zeichen.
UnmanagedType.LPStr Ein Zeiger auf ein null-beendetes Array von ANSI-Zeichen.
UnmanagedType.LPWStr Ein Zeiger auf ein mit Null beendetes Array von Unicode-Zeichen.

Diese Tabelle gilt für String. Für StringBuilder, die einzigen zulässigen Optionen sind UnmanagedType.LPStr und UnmanagedType.LPWStr.

Das folgende Beispiel zeigt Zeichenfolgen, die in der IStringWorker Schnittstelle deklariert sind.

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

Das folgende Beispiel zeigt die entsprechende Schnittstelle, die in einer Typbibliothek beschrieben wird.

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

Zeichenfolgen, die im Plattform-Aufruf verwendet werden

Wenn "CharSet" Unicode oder ein Zeichenfolgenargument explizit als [MarshalAs(UnmanagedType.LPWSTR)] gekennzeichnet ist und die Zeichenfolge nach Wert (nicht ref oder out) übergeben wird, wird die Zeichenfolge angeheftet und direkt durch systemeigenem Code verwendet. Andernfalls ruft die Plattform Zeichenfolgenargumente auf, und konvertieren Sie aus dem .NET Framework-Format (Unicode) in das nicht verwaltete Plattformformat. Zeichenfolgen sind unveränderlich und werden nicht aus nicht verwaltetem Speicher in verwalteten Speicher kopiert, wenn der Aufruf zurückgegeben wird.

Systemeigener Code ist nur für das Freigeben des Speichers verantwortlich, wenn die Zeichenfolge per Verweis übergeben wird und einen neuen Wert zuweist. Andernfalls besitzt die .NET-Laufzeit den Arbeitsspeicher und gibt sie nach dem Aufruf frei.

In der folgenden Tabelle sind die Marshalloptionen für Zeichenfolgen aufgeführt, wenn sie als Methodenargument eines Plattformaufrufs ge marshallt werden. Das MarshalAsAttribute Attribut stellt mehrere UnmanagedType Enumerationswerte für Marshallzeichenfolgen bereit.

Enumerationstyp Beschreibung des nicht verwalteten Formats
UnmanagedType.AnsiBStr Eine COM-Formatvorlage BSTR mit einer präfixierten Länge und ANSI-Zeichen.
UnmanagedType.BStr Eine COM-Formatvorlage BSTR mit einer präfixierten Länge und Unicode-Zeichen.
UnmanagedType.LPStr (Standardwert) Ein Zeiger auf ein null-beendetes Array von ANSI-Zeichen.
UnmanagedType.LPTStr Ein Zeiger auf ein durch Null beendetes Array von plattformabhängigen Zeichen.
UnmanagedType.LPUTF8Str Ein Zeiger auf ein null-beendetes Array mit UTF-8-codierten Zeichen.
UnmanagedType.LPWStr Ein Zeiger auf ein mit Null beendetes Array von Unicode-Zeichen.
UnmanagedType.TBStr Eine COM-Formatvorlage BSTR mit einer präfixierten Länge und plattformabhängigen Zeichen.
VBByRefStr Ein Wert, der es Visual Basic ermöglicht, eine Zeichenfolge in nicht verwaltetem Code zu ändern und die Ergebnisse in verwaltetem Code widerzuspiegeln. Dieser Wert wird nur für den Plattform-Aufruf unterstützt. Dies ist der Standardwert in Visual Basic für ByVal Zeichenfolgen.

Diese Tabelle gilt für String. Für StringBuilder, die einzigen zulässigen Optionen sind LPStr, LPTStrund LPWStr.

Die folgende Typdefinition zeigt die richtige Verwendung für MarshalAsAttribute Plattformaufrufe.

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

In Strukturen verwendete Zeichenfolgen

Zeichenfolgen sind gültige Elemente von Strukturen; StringBuilder Puffer sind jedoch in Strukturen ungültig. In der folgenden Tabelle sind die Marshalloptionen für den String Datentyp aufgeführt, wenn der Typ als Feld ge marshallt wird. Das MarshalAsAttribute Attribut stellt mehrere UnmanagedType Enumerationswerte zum Marshallen von Zeichenfolgen für ein Feld bereit.

Enumerationstyp Beschreibung des nicht verwalteten Formats
UnmanagedType.BStr Eine COM-Formatvorlage BSTR mit einer präfixierten Länge und Unicode-Zeichen.
UnmanagedType.LPStr (Standardwert) Ein Zeiger auf ein null-beendetes Array von ANSI-Zeichen.
UnmanagedType.LPTStr Ein Zeiger auf ein durch Null beendetes Array von plattformabhängigen Zeichen.
UnmanagedType.LPUTF8Str Ein Zeiger auf ein null-beendetes Array mit UTF-8-codierten Zeichen.
UnmanagedType.LPWStr Ein Zeiger auf ein mit Null beendetes Array von Unicode-Zeichen.
UnmanagedType.ByValTStr Ein Array mit zeichen fester Länge; der Typ des Arrays wird durch den Zeichensatz der enthaltenden Struktur bestimmt.

Der ByValTStr Typ wird für Inline-Zeichenarrays mit fester Länge verwendet, die in einer Struktur angezeigt werden. Andere Typen gelten für Zeichenfolgenverweise, die in Strukturen enthalten sind, die Zeiger auf Zeichenfolgen enthalten.

Das CharSet Argument, StructLayoutAttribute das auf die enthaltende Struktur angewendet wird, bestimmt das Zeichenformat von Zeichenfolgen in Strukturen. Die folgenden Beispielstrukturen enthalten Zeichenfolgenverweise und Inlinezeichenfolgen sowie ANSI-, Unicode- und plattformabhängige Zeichen. Die Darstellung dieser Strukturen in einer Typbibliothek wird im folgenden C++-Code gezeigt:

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

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

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

Das folgende Beispiel zeigt, wie Sie die MarshalAsAttribute gleiche Struktur in verschiedenen Formaten definieren.

[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

Zeichenfolgenpuffer mit fester Länge

Unter bestimmten Umständen muss ein Zeichenpuffer mit fester Länge an nicht verwalteten Code übergeben werden, um bearbeitet zu werden. Die einfache Übergabe einer Zeichenfolge funktioniert in diesem Fall nicht, da der Angerufene den Inhalt des übergebenen Puffers nicht ändern kann. Auch wenn die Zeichenfolge per Verweis übergeben wird, gibt es keine Möglichkeit, den Puffer mit einer bestimmten Größe zu initialisieren.

Die Lösung besteht darin, eine byte[] oder char[], je nach erwarteter Codierung, als Argument anstelle eines .String Das Array kann, wenn es mit gekennzeichnet [Out]ist, vom Angerufenen abgeleitet und geändert werden, vorausgesetzt, es überschreitet nicht die Kapazität des zugeordneten Arrays.

Die Windows-API-Funktion GetWindowText (definiert in winuser.h) erfordert beispielsweise, dass der Aufrufer einen Puffer mit fester Länge übergibt, in den die Funktion den Text des Fensters schreibt. Das lpString Argument verweist auf einen vom Aufrufer zugewiesenen Puffer der Größe nMaxCount. Der Aufrufer wird erwartet, dass der Puffer zugewiesen und das nMaxCount Argument auf die Größe des zugeordneten Puffers festgelegt wird. Das folgende Beispiel zeigt die GetWindowText Funktionsdeklaration gemäß der Definition in 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[] kann vom Angerufenen abgeleitet und geändert werden. Im folgenden Codebeispiel wird veranschaulicht, wie ArrayPool<char> sie verwendet werden können, um eine 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

Eine weitere Lösung besteht darin, anstelle eines Arguments Stringein StringBuilder Argument zu übergeben. Der beim Marshallen eines StringBuilder Puffers erstellte Puffer kann vom Angerufenen abgeleitet und geändert werden, sofern er die Kapazität des StringBuilderAngerufenen nicht überschreitet. Sie kann auch auf eine feste Länge initialisiert werden. Wenn Sie z. B. einen StringBuilder Puffer in eine Kapazität Ninitialisieren, stellt der Marshaller einen Puffer mit Größenzeichen (N+1) bereit. Mit +1 wird die Tatsache erfasst, dass die nicht verwaltete Zeichenfolge einen NULL-Terminator aufweist, während StringBuilder dies nicht der Fall ist.

Hinweis

Im Allgemeinen wird das Übergeben von StringBuilder Argumenten nicht empfohlen, wenn Sie sich gedanken über die Leistung machen. Weitere Informationen finden Sie unter String-Parameter.

Siehe auch