Dela via


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 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 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å Nger 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.

Se även