Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Sowohl die System.String-Klassen als auch die System.Text.StringBuilder-Klassen weisen ein ähnliches Marshallverhalten auf.
Zeichenfolgen werden als BSTR-Typ im COM-Format oder als eine auf NULL endende Zeichenfolge (ein Zeichenarray, das mit dem Zeichen NULL endet) gemarshallt. Die Zeichen innerhalb der Zeichenfolge können als Unicode (Standardeinstellung auf Windows-Systemen) oder ANSI gemarshallt werden.
In Schnittstellen verwendete Strings
In der folgenden Tabelle werden die Marshallingoptionen für den Zeichenfolgen-Datentyp aufgelistet, wenn dieser als Methodenargument für nicht verwalteten Code gemarshallt wird. Das MarshalAsAttribute-Attribut stellt mehrere UnmanagedType-Aufzählungswerte für das Marshallen von Zeichenfolgen an 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 mit NULL endendes 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);
};
Im Plattformaufruf verwendete Zeichenfolgen
Wenn der CharSet-Wert Unicode ist oder ein Zeichenfolgenargument explizit als [MarshalAs(UnmanagedType.LPWSTR)] gekennzeichnet ist und die Zeichenfolge als Wert (nicht ref oder out) übergeben wird, wird die Zeichenfolge fixiert und direkt durch nativen Code verwendet. Andernfalls kopiert der Plattformaufruf Zeichenfolgenargumente, wobei das .NET Framework-Format (Unicode) in das nicht verwaltete Plattformformat konvertiert wird. 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 dabei ein neuer Wert zugewiesen wird. Andernfalls besitzt die .NET-Laufzeit den Arbeitsspeicher und gibt sie nach dem Aufruf frei.
In der folgenden Tabelle werden die Marshallingoptionen für Zeichenfolgen aufgelistet, wenn die Zeichenfolgen als Methodenargumente eines Plattformaufrufs gemarshallt werden. Das MarshalAsAttribute-Attribut stellt mehrere UnmanagedType-Enumerationswerte zum Marshallen von Zeichenfolgen 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 mit NULL endendes 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 |
Ein COM-Stil BSTR mit vorgegebener Länge und plattformspezifischen 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 Plattformaufrufe 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 von MarshalAsAttribute für Plattformaufrufaufrufe.
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 werden die Marshalling-Optionen für den String Datentyp angezeigt, wenn der Typ als Feld gemarshallt wird. Das MarshalAsAttribute Attribut stellt mehrere UnmanagedType Aufzählungswerte zum Überführen von Zeichenfolgen zu einem 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 mit NULL endendes 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 Zeichen-Array mit fester Länge; der Typ des Arrays wird durch den Zeichensatz der Struktur, die es enthält, 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 des StructLayoutAttribute, das auf die enthaltene Struktur angewendet wird, bestimmt die Zeichenformatierung von Zeichenfolgen in den 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 man die Struktur mit MarshalAsAttribute in verschiedenen Formaten definiert.
[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, byte[] oder char[] je nach erwarteter Codierung zu übergeben, und zwar als Argument anstelle eines String. Das Array kann, wenn es mit [Out] gekennzeichnet ist, vom Angerufenen dereferenziert und geändert werden, vorausgesetzt, es überschreitet nicht die Kapazität des zugewiesenen 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 sollte den Puffer zuweisen und das nMaxCount Argument auf die Größe des zugewiesenen Puffers festlegen. 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.
);
char[] kann vom Aufgerufenen dereferenziert und geändert werden. Im folgenden Codebeispiel wird veranschaulicht, wie ArrayPool<char> verwendet werden kann, um eine char[] vorab zuzuweisen.
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 andere Lösung besteht darin, StringBuilder als Argument anstelle von String zu übergeben. Der beim Marshallen eines StringBuilder erstellte Puffer kann vom Angerufenen dereferenziert und geändert werden, sofern er die Kapazität des StringBuilder nicht überschreitet. Sie kann auch auf eine feste Länge initialisiert werden. Wenn Sie z. B. einen StringBuilder Puffer auf eine Kapazität von N initialisieren, stellt der Marshaller einen Puffer mit der Größe von (N+1) Zeichen 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 die Leistung nicht beeinträchtigt werden soll. Weitere Informationen finden Sie unter String-Parameter.