Standardmarshalling für Zeichenfolgen
Die System.String-Klasse und die System.Text.StringBuilder-Klasse weisen ein ähnliches Marshallingverhalten 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 Zeichenfolgen
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-Enumerationswerte zum Marshallen von Zeichenfolgen an COM-Schnittstellen bereit.
Enumerationstyp | Beschreibung des nicht verwalteten Formats |
---|---|
UnmanagedType.BStr (Standard) |
BSTR im COM-Format mit vorangestellter 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 endendes Array von Unicode-Zeichen. |
Diese Tabelle gilt für String. Für StringBuilder sind nur die Optionen UnmanagedType.LPStr
und UnmanagedType.LPWStr
zulässig.
Im folgenden Beispiel werden in der IStringWorker
Schnittstelle deklarierte Zeichenfolgen gezeigt.
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
Im folgenden Beispiel ist die entsprechende Schnittstelle dargestellt, 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 es sich um einen Unicode-Zeichensatz handelt oder ein Zeichenfolgenargument explizit als [MarshalAs(UnmanagedType.LPWSTR)] gekennzeichnet ist und die Zeichenfolge nach Wert übergeben wird (nicht ref
oder out
), wird die Zeichenfolge angeheftet und direkt von nativem 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 bei Rückgabe des Aufrufs nicht aus dem nicht verwalteten Speicher in den verwalteten Speicher zurückkopiert.
Nativer Code ist nur dann für die Freigabe des Arbeitsspeichers verantwortlich, wenn die Zeichenfolge als Verweis übergeben und ein neuer Wert zugewiesen wird. Andernfalls ist die .NET-Runtime Besitzer des Arbeitsspeichers und gibt ihn nach dem Aufruf wieder 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 |
BSTR im COM-Format mit vorangestellter Länge und ANSI-Zeichen. |
UnmanagedType.BStr |
BSTR im COM-Format mit vorangestellter 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 mit NULL endendes Array von plattformabhängigen Zeichen. |
UnmanagedType.LPUTF8Str |
Ein Zeiger auf ein mit Null endendes Array von UTF-8-codierten Zeichen. |
UnmanagedType.LPWStr |
Ein Zeiger auf ein mit Null endendes Array von Unicode-Zeichen. |
UnmanagedType.TBStr |
BSTR im COM-Format mit vorangestellter 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 wiederzugeben. Dieser Wert wird nur für Plattformaufrufe unterstützt. Das ist der Standardwert in Visual Basic für ByVal -Zeichenfolgen. |
Diese Tabelle gilt für String. Für StringBuilder sind nur die Optionen LPStr
, LPTStr
und LPWStr
zulässig.
In der folgenden Typdefinition ist die richtige Verwendung von MarshalAsAttribute
für Plattformaufrufe dargestellt.
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 Member von Strukturen. StringBuilder-Puffer sind jedoch in Strukturen ungültig. In der folgenden Tabelle werden die Marshallingoptionen für den String-Datentyp aufgelistet, wenn der Typ als Feld gemarshallt wird. Das MarshalAsAttribute-Attribut stellt mehrere UnmanagedType-Enumerationswerte zum Marshallen von Zeichenfolgen an ein Feld bereit.
Enumerationstyp | Beschreibung des nicht verwalteten Formats |
---|---|
UnmanagedType.BStr |
BSTR im COM-Format mit vorangestellter 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 mit NULL endendes Array von plattformabhängigen Zeichen. |
UnmanagedType.LPUTF8Str |
Ein Zeiger auf ein mit Null endendes Array von UTF-8-codierten Zeichen. |
UnmanagedType.LPWStr |
Ein Zeiger auf ein mit Null endendes Array von Unicode-Zeichen. |
UnmanagedType.ByValTStr |
Ein Zeichenarray mit fester Länge. Der Typ des Arrays wird durch den Zeichensatz der enthaltenden Struktur bestimmt. |
Der ByValTStr
-Typ wird für Inlinearrays mit Zeichen fester Länge verwendet, die in einer Struktur dargestellt 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 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 ist im folgenden Code in C++ dargestellt:
struct StringInfoA
{
char * f1;
char f2[256];
};
struct StringInfoW
{
WCHAR * f1;
WCHAR f2[256];
BSTR f3;
};
struct StringInfoT
{
TCHAR * f1;
TCHAR f2[256];
};
Im folgenden Beispiel wird veranschaulicht, wie das MarshalAsAttribute zum Definieren derselben Struktur in unterschiedlichen Formaten verwendet wird.
[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 müssen Zeichenpuffer mit fester Länge zur Bearbeitung an nicht verwalteten Code übergeben werden. In diesem Fall ist es nicht möglich, die Zeichenfolge einfach zu übergeben, weil der Aufgerufene den Inhalt des übergebenen Puffers nicht ändern kann. Auch wenn die Zeichenfolge als Verweis übergeben wird, kann der Puffer nicht mit einer bestimmten Größe initialisiert werden.
Die Lösung besteht darin, byte[]
oder char[]
je nach erwarteter Codierung zu übergeben, und zwar als Argument anstelle eines String. Das Array, wenn es mit [Out]
gekennzeichnet ist, kann durch den Aufgerufenen dereferenziert und geändert werden, sofern die Kapazität des zugeordneten Arrays nicht überschritten wird.
Zum Beispiel ist es bei der GetWindowText
-API-Funktion von Windows (definiert in winuser.h) erforderlich, dass der Aufrufer einen Zeichenpuffer fester Länge übergibt, in den die Funktion den Text des Fensters schreibt. Das Argument lpString
zeigt auf einen vom Aufrufer reservierten Puffer der Größe nMaxCount
. Der Aufrufer soll den Puffer reservieren und das nMaxCount
-Argument auf die Größe des reservierten Puffers festlegen. Das folgende Beispiel stellt die GetWindowText
-Funktionsdeklaration entsprechend der Definition in winuser.h dar.
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 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 Marshalling eines StringBuilder
erstellte Puffer kann durch den Aufgerufenen dereferenziert und geändert werden, sofern die Kapazität von StringBuilder
nicht überschritten wird. Er kann auch mit einer festen Länge initialisiert werden. Wenn Sie beispielsweise einen StringBuilder
-Puffer mit einer Kapazität von N
initialisieren, stellt der Marshaller einen Puffer mit einer Größe von (N
+1) Zeichen zur Verfügung. Durch +1 wird der Tatsache Rechnung getragen, dass die nicht verwaltete Zeichenfolge (im Gegensatz zu StringBuilder
) über einen NULL-Terminator verfügt.
Hinweis
Im Allgemeinen wird das Übergeben von StringBuilder
-Argumenten nicht empfohlen, wenn die Leistung nicht beeinträchtigt werden soll. Weitere Informationen finden Sie unter Zeichenfolgenparameter.