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 , LPStr
en LPTStr
LPWStr
.
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.