Condividi tramite


Marshalling predefinito per le stringhe

Entrambe le classi System.String e System.Text.StringBuilder hanno un simile comportamento di marshalling.

Le stringhe sono marshallate come tipo BSTR in stile COM o come stringa terminata da null (un array di caratteri che termina con un carattere null). I caratteri all'interno della stringa possono essere distribuiti come Unicode (impostazione predefinita nei sistemi Windows) o ANSI.

Stringhe usate nelle interfacce

Nella tabella seguente vengono illustrate le opzioni di marshalling per il tipo di dati stringa quando viene eseguito il marshalling come argomento del metodo al codice non gestito. L'attributo MarshalAsAttribute fornisce diversi UnmanagedType valori di enumerazione per effettuare il marshalling delle stringhe alle interfacce COM.

Tipo di enumerazione Descrizione del formato non gestito
UnmanagedType.BStr (impostazione predefinita) Uno stile COM BSTR con lunghezza prefissata e caratteri Unicode.
UnmanagedType.LPStr Puntatore a una matrice con terminazione Null di caratteri ANSI.
UnmanagedType.LPWStr Puntatore a una matrice con terminazione Null di caratteri Unicode.

Questa tabella si applica a String. Per StringBuilder, le uniche opzioni consentite sono UnmanagedType.LPStr e UnmanagedType.LPWStr.

Nell'esempio seguente vengono illustrate le stringhe dichiarate nell'interfaccia IStringWorker .

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

Nell'esempio seguente viene illustrata l'interfaccia corrispondente descritta in una libreria dei tipi.

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);
};

Stringhe usate in P/Invoke

Quando il CharSet è impostato su Unicode o un argomento stringa viene contrassegnato in modo esplicito come [MarshalAs(UnmanagedType.LPWSTR)] e la stringa viene passata come valore (non ref o out), la stringa viene bloccata e utilizzata direttamente dal codice nativo. In caso contrario, platform invoke copia gli argomenti stringa, convertendo dal formato .NET Framework (Unicode) al formato non gestito della piattaforma. Le stringhe non sono modificabili e non vengono copiate dalla memoria non gestita alla memoria gestita quando la chiamata viene restituita.

Il codice nativo è responsabile solo del rilascio della memoria quando la stringa viene passata per riferimento e assegna un nuovo valore. In caso contrario, il runtime .NET possiede la memoria e la rilascia dopo la chiamata.

Nella tabella seguente sono elencate le opzioni di marshalling per le stringhe quando vengono eseguite come argomento del metodo di una chiamata di invocazione della piattaforma. L'attributo MarshalAsAttribute fornisce diversi UnmanagedType valori di enumerazione per effettuare il marshalling delle stringhe.

Tipo di enumerazione Descrizione del formato non gestito
UnmanagedType.AnsiBStr Stile COM BSTR con lunghezza prefissata e caratteri ANSI.
UnmanagedType.BStr Uno stile COM BSTR con lunghezza prefissata e caratteri Unicode.
UnmanagedType.LPStr (impostazione predefinita) Puntatore a una matrice con terminazione Null di caratteri ANSI.
UnmanagedType.LPTStr Puntatore a una matrice con terminazione Null di caratteri dipendenti dalla piattaforma.
UnmanagedType.LPUTF8Str Puntatore a una matrice di caratteri codificati in UTF-8 terminata da un valore zero.
UnmanagedType.LPWStr Puntatore a una matrice con terminazione Null di caratteri Unicode.
UnmanagedType.TBStr Stile COM BSTR con una lunghezza prefissata e caratteri dipendenti dalla piattaforma.
VBByRefStr Valore che consente a Visual Basic di modificare una stringa nel codice non gestito e di riflettere i risultati nel codice gestito. Questo valore è supportato solo per platform invoke. Questo è il valore predefinito in Visual Basic per ByVal le stringhe.

Questa tabella si applica a String. Per StringBuilder, le uniche opzioni consentite sono LPStr, LPTStre LPWStr.

La definizione di tipo seguente mostra l'uso corretto di MarshalAsAttribute per le chiamate platform invoke.

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

Stringhe usate nelle strutture

Le stringhe sono membri validi di strutture; Tuttavia, StringBuilder i buffer non sono validi nelle strutture. Nella tabella seguente vengono illustrate le opzioni di marshalling per il String tipo di dati quando il tipo viene sottoposto a marshalling come campo. L'attributo MarshalAsAttribute fornisce diversi UnmanagedType valori di enumerazione per effettuare il marshalling delle stringhe in un campo.

Tipo di enumerazione Descrizione del formato non gestito
UnmanagedType.BStr Uno stile COM BSTR con lunghezza prefissata e caratteri Unicode.
UnmanagedType.LPStr (impostazione predefinita) Puntatore a una matrice con terminazione Null di caratteri ANSI.
UnmanagedType.LPTStr Puntatore a una matrice con terminazione Null di caratteri dipendenti dalla piattaforma.
UnmanagedType.LPUTF8Str Puntatore a una matrice di caratteri codificati in UTF-8 terminata da un valore zero.
UnmanagedType.LPWStr Puntatore a una matrice con terminazione Null di caratteri Unicode.
UnmanagedType.ByValTStr Matrice a lunghezza fissa di caratteri; il tipo della matrice è determinato dal set di caratteri della struttura contenitore.

Il ByValTStr tipo viene usato per matrici di caratteri a lunghezza fissa inline visualizzate all'interno di una struttura. Altri tipi si applicano ai riferimenti stringa contenuti all'interno di strutture che contengono puntatori alle stringhe.

L'argomento CharSet della funzione StructLayoutAttribute applicata alla struttura contenitore determina il formato dei caratteri delle stringhe nelle strutture. Le strutture di esempio seguenti contengono riferimenti a stringhe e stringhe inline, nonché caratteri ANSI, Unicode e dipendenti dalla piattaforma. La rappresentazione di queste strutture in una libreria dei tipi è illustrata nel codice C++ seguente:

struct StringInfoA
{
    char *  f1;
    char    f2[256];
};

struct StringInfoW
{
    WCHAR * f1;
    WCHAR   f2[256];
    BSTR    f3;
};

struct StringInfoT
{
    TCHAR * f1;
    TCHAR   f2[256];
};

Nell'esempio seguente viene illustrato come usare MarshalAsAttribute per definire la stessa struttura in formati diversi.

[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

Buffer di stringhe a lunghezza fissa

In alcuni casi, è necessario passare un buffer di caratteri a lunghezza fissa nel codice non gestito da modificare. Il semplice passaggio di una stringa non funziona in questo caso perché il chiamato non può modificare il contenuto del buffer passato. Anche se la stringa viene passata per riferimento, non è possibile inizializzare il buffer in una determinata dimensione.

La soluzione consiste nel passare un byte[] o un char[], a seconda della codifica prevista, come argomento anziché un String. La matrice, se contrassegnata con [Out], può essere dereferenziata e modificata dal chiamato, purché non superi la capacità della matrice allocata.

Ad esempio, la funzione API Windows GetWindowText (definita in winuser.h) richiede che il chiamante passi un buffer di caratteri a lunghezza fissa a cui la funzione scrive il testo della finestra. L'argomento lpString punta a un buffer allocato dal chiamante di dimensioni nMaxCount. Il chiamante deve allocare il buffer e impostare l'argomento nMaxCount alla dimensione del buffer allocato. L'esempio seguente mostra la dichiarazione di GetWindowText funzione come definita in winuser.h.

int GetWindowText(
    HWND hWnd,        // Handle to window or control.
    LPTStr lpString,  // Text buffer.
    int nMaxCount     // Maximum number of characters to copy.
);

Un char[] oggetto può essere dereferenziato e modificato dal chiamato. Nell'esempio di codice seguente viene illustrato come ArrayPool<char> possa essere usato per pre-allocare un 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

Un'altra soluzione consiste nel passare un StringBuilder come argomento anziché un String. Il buffer creato durante il marshalling di un StringBuilder può essere dereferenziato e modificato dal chiamato, purché non superi la capacità di StringBuilder. Può anche essere inizializzato a lunghezza fissa. Ad esempio, se si inizializza un StringBuilder buffer con una capacità di N, il marshaller fornisce un buffer di caratteri con dimensione (N+1). +1 indica il fatto che la stringa non gestita ha un carattere di terminazione Null mentre StringBuilder non lo è.

Annotazioni

In generale, il passaggio di StringBuilder argomenti non è consigliato se si è preoccupati per le prestazioni. Per altre informazioni, vedere Parametri stringa.

Vedere anche