Standard marshalling för strängar
Både klasserna System.String och System.Text.StringBuilder har liknande marshallingbeteende.
Strängar ordnas som en COM-typ BSTR
eller som en null-avslutad sträng (en teckenmatris som slutar med ett null-tecken). Tecknen i strängen kan ordnas som Unicode (standard på Windows-system) eller ANSI.
Strängar som används i gränssnitt
I följande tabell visas marshallingsalternativen för strängdatatypen när de är ordnade som ett metodargument till ohanterad kod. Attributet MarshalAsAttribute innehåller flera UnmanagedType uppräkningsvärden för att konvertera strängar till COM-gränssnitt.
Uppräkningstyp | Beskrivning av ohanterat format |
---|---|
UnmanagedType.BStr (standard) |
Ett COM-format BSTR med prefixlängd och Unicode-tecken. |
UnmanagedType.LPStr |
En pekare till en null-avslutad matris med ANSI-tecken. |
UnmanagedType.LPWStr |
En pekare till en null-avslutad matris med Unicode-tecken. |
Den här tabellen gäller för String. För StringBuilderär UnmanagedType.LPStr
de enda tillåtna alternativen och UnmanagedType.LPWStr
.
I följande exempel visas strängar som deklarerats i IStringWorker
gränssnittet.
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
I följande exempel visas motsvarande gränssnitt som beskrivs i ett typbibliotek.
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);
};
Strängar som används i plattformsanrop
När CharSet är Unicode eller ett strängargument uttryckligen markeras som [MarshalAs(UnmanagedType.LPWSTR)] och strängen skickas med värde (inte ref
eller out
), fästs strängen och används direkt av den interna koden. Annars anropar plattformen kopieringssträngsargument och konverterar från .NET Framework-formatet (Unicode) till det ohanterade plattformsformatet. Strängar är oföränderliga och kopieras inte tillbaka från ohanterat minne till hanterat minne när anropet returneras.
Intern kod ansvarar endast för att frigöra minnet när strängen skickas med referens och det tilldelar ett nytt värde. Annars äger .NET-körningen minnet och släpper det efter anropet.
I följande tabell visas marshallingalternativen för strängar när de är ordnade som ett metodargument för ett plattformsanrop. Attributet MarshalAsAttribute innehåller flera UnmanagedType uppräkningsvärden för marskalksträngar.
Uppräkningstyp | Beskrivning av ohanterat format |
---|---|
UnmanagedType.AnsiBStr |
Ett COM-format BSTR med prefixlängd och ANSI-tecken. |
UnmanagedType.BStr |
Ett COM-format BSTR med prefixlängd och Unicode-tecken. |
UnmanagedType.LPStr (standard) |
En pekare till en null-avslutad matris med ANSI-tecken. |
UnmanagedType.LPTStr |
En pekare till en null-avslutad matris med plattformsberoende tecken. |
UnmanagedType.LPUTF8Str |
En pekare till en null-avslutad matris med UTF-8-kodade tecken. |
UnmanagedType.LPWStr |
En pekare till en null-avslutad matris med Unicode-tecken. |
UnmanagedType.TBStr |
Ett COM-format BSTR med prefixlängd och plattformsberoende tecken. |
VBByRefStr |
Ett värde som gör att Visual Basic kan ändra en sträng i ohanterad kod och få resultatet att återspeglas i hanterad kod. Det här värdet stöds endast för plattformsanrop. Det här är standardvärdet i Visual Basic för ByVal strängar. |
Den här tabellen gäller för String. För StringBuilderär LPStr
de enda tillåtna alternativen , LPTStr
och LPWStr
.
Följande typdefinition visar korrekt användning av MarshalAsAttribute
för plattformsanrop.
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
Strängar som används i strukturer
Strängar är giltiga medlemmar i strukturer. Buffertar är dock StringBuilder ogiltiga i strukturer. I följande tabell visas marshallingalternativen String för datatypen när typen är marshalled som ett fält. Attributet MarshalAsAttribute innehåller flera UnmanagedType uppräkningsvärden för att konvertera strängar till ett fält.
Uppräkningstyp | Beskrivning av ohanterat format |
---|---|
UnmanagedType.BStr |
Ett COM-format BSTR med prefixlängd och Unicode-tecken. |
UnmanagedType.LPStr (standard) |
En pekare till en null-avslutad matris med ANSI-tecken. |
UnmanagedType.LPTStr |
En pekare till en null-avslutad matris med plattformsberoende tecken. |
UnmanagedType.LPUTF8Str |
En pekare till en null-avslutad matris med UTF-8-kodade tecken. |
UnmanagedType.LPWStr |
En pekare till en null-avslutad matris med Unicode-tecken. |
UnmanagedType.ByValTStr |
En matris med fast längd med tecken. matrisens typ bestäms av teckenuppsättningen för den innehållande strukturen. |
Typen ByValTStr
används för infogade teckenmatriser med fast längd som visas i en struktur. Andra typer gäller för strängreferenser som finns i strukturer som innehåller pekare till strängar.
Argumentet CharSet
för StructLayoutAttribute det som tillämpas på den innehållande strukturen avgör teckenformatet för strängar i strukturer. Följande exempelstrukturer innehåller strängreferenser och infogade strängar, samt ANSI, Unicode och plattformsberoende tecken. Representationen av dessa strukturer i ett typbibliotek visas i följande C++-kod:
struct StringInfoA
{
char * f1;
char f2[256];
};
struct StringInfoW
{
WCHAR * f1;
WCHAR f2[256];
BSTR f3;
};
struct StringInfoT
{
TCHAR * f1;
TCHAR f2[256];
};
I följande exempel visas hur du använder MarshalAsAttribute för att definiera samma struktur i olika format.
[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
Strängbuffertar med fast längd
I vissa fall måste en buffert med fast längd skickas till ohanterad kod för att manipuleras. Att bara skicka en sträng fungerar inte i det här fallet eftersom anroparen inte kan ändra innehållet i den skickade bufferten. Även om strängen skickas som referens finns det inget sätt att initiera bufferten till en viss storlek.
Lösningen är att skicka en byte[]
eller char[]
, beroende på förväntad kodning, som argument i stället för en String. Matrisen, när den är markerad med [Out]
, kan derefereras och ändras av anroparen, förutsatt att den inte överskrider kapaciteten för den allokerade matrisen.
Windows API-funktionen (definierad i winuser.h) kräver till exempel GetWindowText
att anroparen skickar en buffert med fast längd som funktionen skriver fönstrets text till. Argumentet lpString
pekar på en anropare allokerad buffert av storlek nMaxCount
. Anroparen förväntas allokera bufferten och ange nMaxCount
argumentet till storleken på den allokerade bufferten. I följande exempel visas funktionsdeklarationen GetWindowText
enligt definitionen i 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[]
kan derefereras och ändras av anroparen. I följande kodexempel visas hur ArrayPool<char>
du kan förallokera en 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
En annan lösning är att skicka ett StringBuilder som argument i stället för en String. Bufferten som skapas när en kan StringBuilder
derefereras och ändras av anroparen, förutsatt att den inte överskrider kapaciteten för StringBuilder
. Den kan också initieras till en fast längd. Om du till exempel initierar en StringBuilder
buffert till en kapacitet på N
ger marshallern en buffert med storlekstecken (N
+1). +1 står för det faktum att den ohanterade strängen har en null-avslut medan StringBuilder
den inte gör det.
Kommentar
I allmänhet rekommenderas inte att skicka StringBuilder
argument om du är orolig för prestanda. Mer information finns i Strängparametrar.