Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Både klasserna System.String och System.Text.StringBuilder har liknande hantering av data.
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 hanteras som Unicode (standardvärdet för 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 (standardinställning) |
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 kopierar och konverterar plattformsanrop strängargument från .NET Framework-formatet (Unicode) till plattformens ohanterade format. Strängar är oföränderliga och kopieras inte tillbaka från ohanterat minne till hanterat minne när anropet returneras.
Inbyggd kod ansvarar endast för att frigöra minnet när strängen skickas som en referens och när ett nytt värde tilldelas. Annars äger .NET-körtiden minne och släpper det efter anropet.
I följande tabell listas alternativen för strängmarshaling när dessa används som metodargument vid ett plattformsanrop. Attributet MarshalAsAttribute tillhandahåller flera UnmanagedType uppräkningsvärden för att överföra strängar.
| Uppräkningstyp | Beskrivning av ohanterat format |
|---|---|
UnmanagedType.AnsiBStr |
En COM-stil BSTR med en förprefixad längd och ANSI-tecken. |
UnmanagedType.BStr |
Ett COM-format BSTR med prefixlängd och Unicode-tecken. |
UnmanagedType.LPStr (standardinställning) |
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 det möjligt för Visual Basic att ä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 LPStrde enda tillåtna alternativen , LPTStroch 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 för datatypen String när typen hanteras 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 (standardinställning) |
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 det StructLayoutAttribute som tillämpas på den innehållande strukturen avgör teckenformatet för strängarna i strukturerna. 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.
Till exempel kräver funktionen Windows GetWindowText API (definierad i winuser.h) 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. Den rekommenderade metoden är att använda ArrayPool<T> för att hyra en char[], vilket undviker upprepade heap-allokeringar. Följande kodexempel visar det här mönstret.
using System;
using System.Buffers;
using System.Runtime.InteropServices;
internal static class NativeMethods
{
[DllImport("User32.dll", CharSet = CharSet.Unicode)]
public static extern int 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);
try
{
int length = NativeMethods.GetWindowText(h, buffer, buffer.Length);
return new string(buffer, 0, length);
}
finally
{
ArrayPool<char>.Shared.Return(buffer);
}
}
}
Imports System
Imports System.Buffers
Imports System.Runtime.InteropServices
Friend Class NativeMethods
Public Declare Auto Function GetWindowText Lib "User32.dll" _
(hWnd As IntPtr, <Out> lpString() As Char, nMaxCount As Integer) 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)
Try
Dim length As Integer = NativeMethods.GetWindowText(h, buffer, buffer.Length)
Return New String(buffer, 0, length)
Finally
ArrayPool(Of Char).Shared.Return(buffer)
End Try
End Function
End Class
Du kan också överväga att skicka en StringBuilder i stället för en String. Bufferten som skapas när en StringBuilder är marshalled kan avrefereras 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 med en kapacitet på N, ger marshalleren en buffert med storlek (N+1) tecken. +1 står för det faktum att den ohanterade strängen har en null-terminator medan StringBuilder saknar det.
Försiktighet
Undvik StringBuilder parametrar när prestanda är viktiga. Marshalling a StringBuilder skapar alltid en intern buffertkopia. Ett typiskt anrop för att hämta en sträng ur den interna koden kan resultera i fyra allokeringar:
- En hanterad
StringBuilderbuffert. - En inbyggd buffert som allokerats under sammanställning.
- Om
[Out]kopieras det ursprungliga buffertinnehållet till en nyligen allokerad hanterad array. - En
stringallokerad avToString().
Om du återanvänder samma StringBuilder mellan anrop sparas bara en allokering. Att använda en teckenbuffert som hyrts från ArrayPool<char> är mycket mer effektivt, eftersom det minskar efterföljande anrop till bara allokeringen för ToString().
Dessutom StringBuilder innehåller kapaciteten inte en dold noll-terminator, som interop alltid står för. Det här är ett vanligt misstag eftersom de flesta API:er vill ha buffertens storlek , inklusive null. Detta kan resultera i bortkastade eller onödiga allokeringar, och det förhindrar programkörningen från att optimera StringBuilder marshalling för att minimera kopior.
Mer information finns i Strängparametrar och CA1838: Undvik StringBuilder parametrar för P/Invokes.