Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Les classes System.String et System.Text.StringBuilder présentent un comportement de marshaling semblable.
Les chaînes sont marshalées en tant que type COM BSTR ou en tant que chaîne terminée par null (tableau de caractères qui se termine par un caractère Null). Les caractères de la chaîne peuvent être marshalés en unicode (la valeur par défaut sur les systèmes Windows) ou ANSI.
Chaînes utilisées dans les interfaces
Le tableau suivant montre les options de marshaling pour le type de données String quand il est marshalé comme un argument de méthode du code non managé. L’attribut MarshalAsAttribute fournit plusieurs UnmanagedType valeurs d’énumération pour marshaller des chaînes vers des interfaces COM.
| Type d’énumération | Description du format non managé |
|---|---|
UnmanagedType.BStr (valeur par défaut) |
Style COM BSTR avec une longueur préfixée et des caractères Unicode. |
UnmanagedType.LPStr |
Pointeur vers un tableau terminé par un caractère nul de caractères ANSI. |
UnmanagedType.LPWStr |
Pointeur vers un tableau de caractères Unicode terminé par null. |
Ce tableau s’applique à String. Pour StringBuilder, les seules options autorisées sont UnmanagedType.LPStr et UnmanagedType.LPWStr.
L’exemple suivant montre les chaînes déclarées dans l’interface 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
L’exemple suivant montre l’interface correspondante décrite dans une bibliothèque de types.
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);
};
Chaînes utilisées dans les appels de code non managé
Lorsque CharSet est Unicode ou qu’un argument de chaîne est explicitement marqué comme [MarshalAs(UnmanagedType.LPWSTR)] et que la chaîne est passée par valeur (non ref ou out), la chaîne est épinglée et utilisée directement par le code natif. Sinon, l’appel de code non managé copie des arguments de chaîne, en convertissant du format .NET Framework (Unicode) au format non managé de la plateforme. Les chaînes sont immuables et ne sont pas copiées de la mémoire non managée vers la mémoire managée lorsque l’appel retourne.
Le code natif est uniquement responsable de la libération de la mémoire lorsque la chaîne est passée par référence et qu’elle affecte une nouvelle valeur. Sinon, le runtime .NET possède la mémoire et la libère après l’appel.
Le tableau suivant répertorie les options de marshaling pour les chaînes quand celles-ci sont marshalées comme un argument de méthode d’un appel de code non managé. L'attribut MarshalAsAttribute fournit plusieurs valeurs d'énumération UnmanagedType pour marshaler les chaînes.
| Type d’énumération | Description du format non managé |
|---|---|
UnmanagedType.AnsiBStr |
Style COM BSTR avec une longueur préfixée et des caractères ANSI. |
UnmanagedType.BStr |
Style COM BSTR avec une longueur préfixée et des caractères Unicode. |
UnmanagedType.LPStr (valeur par défaut) |
Pointeur vers un tableau terminé par un caractère nul de caractères ANSI. |
UnmanagedType.LPTStr |
Pointeur vers un tableau de caractères dépendant de la plateforme se terminant par un caractère Null. |
UnmanagedType.LPUTF8Str |
Pointeur vers un tableau de caractères encodés UTF-8 terminé par null. |
UnmanagedType.LPWStr |
Pointeur vers un tableau de caractères Unicode terminé par null. |
UnmanagedType.TBStr |
Style COM BSTR avec une longueur préfixée et des caractères dépendants de la plateforme. |
VBByRefStr |
Valeur qui permet à Visual Basic de modifier une chaîne dans du code non managé et de refléter les résultats dans le code managé. Cette valeur est prise en charge uniquement pour l'appel de code non managé. Il s’agit de la valeur par défaut dans Visual Basic pour ByVal les chaînes. |
Ce tableau s’applique à String. Pour StringBuilder, les seules options autorisées sont LPStr, LPTStret LPWStr.
La définition de type suivante montre l'utilisation correcte de MarshalAsAttribute pour les appels de plateforme d'invocation.
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
Chaînes utilisées dans les structures
Les chaînes sont des membres valides de structures ; toutefois, StringBuilder les mémoires tampons ne sont pas valides dans les structures. Le tableau suivant montre les options de marshaling pour le type de données String lorsque le type est marshalé en tant que champ. L'attribut MarshalAsAttribute fournit plusieurs UnmanagedType valeurs d'énumération pour convertir des chaînes en un champ.
| Type d’énumération | Description du format non managé |
|---|---|
UnmanagedType.BStr |
Style COM BSTR avec une longueur préfixée et des caractères Unicode. |
UnmanagedType.LPStr (valeur par défaut) |
Pointeur vers un tableau terminé par un caractère nul de caractères ANSI. |
UnmanagedType.LPTStr |
Pointeur vers un tableau de caractères dépendant de la plateforme se terminant par un caractère Null. |
UnmanagedType.LPUTF8Str |
Pointeur vers un tableau de caractères encodés UTF-8 terminé par null. |
UnmanagedType.LPWStr |
Pointeur vers un tableau de caractères Unicode terminé par null. |
UnmanagedType.ByValTStr |
Tableau de caractères de longueur fixe ; Le type du tableau est déterminé par le jeu de caractères de la structure conteneur. |
Le ByValTStr type est utilisé pour les tableaux de caractères inline et de longueur fixe qui apparaissent dans une structure. D’autres types s’appliquent aux références de chaîne contenues dans des structures qui contiennent des pointeurs vers des chaînes.
L’argument CharSet de l’élément StructLayoutAttribute appliqué à la structure conteneur détermine le format de caractères des chaînes dans les structures. Les exemples de structures suivants contiennent des références de chaîne et des chaînes inline, ainsi que des caractères ANSI, Unicode et dépendant de la plateforme. La représentation de ces structures dans une bibliothèque de types est illustrée dans le code C++ suivant :
struct StringInfoA
{
char * f1;
char f2[256];
};
struct StringInfoW
{
WCHAR * f1;
WCHAR f2[256];
BSTR f3;
};
struct StringInfoT
{
TCHAR * f1;
TCHAR f2[256];
};
L’exemple suivant montre comment utiliser la MarshalAsAttribute structure pour définir la même structure dans différents formats.
[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
Mémoires tampons de chaînes de longueur fixe
Dans certaines circonstances, une mémoire tampon de caractères de longueur fixe doit être passée dans du code non managé à manipuler. Le simple passage d’une chaîne ne fonctionne pas dans ce cas, car l’appelé ne peut pas modifier le contenu de la mémoire tampon passée. Même si la chaîne est passée par référence, il n’existe aucun moyen d’initialiser la mémoire tampon à une taille donnée.
La solution consiste à passer un byte[] ou char[], selon l’encodage attendu, en tant qu’argument au lieu d’un String. Le tableau, lorsqu’il est marqué avec [Out], peut être déréférencé et modifié par l'appelant, à condition qu’il ne dépasse pas la capacité du tableau alloué.
Par exemple, la fonction API Windows GetWindowText (définie dans winuser.h) nécessite que l’appelant passe une mémoire tampon de caractères de longueur fixe dans laquelle la fonction écrit le texte de la fenêtre. L’argument lpString pointe vers une mémoire tampon allouée par l’appelant de taille nMaxCount. L’appelant est censé allouer la mémoire tampon et définir l’argument nMaxCount sur la taille de la mémoire tampon allouée. L’exemple suivant montre la GetWindowText déclaration de fonction telle que définie dans 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[] peut être déréférencé et modifié par l’appelé. L’exemple de code suivant montre comment ArrayPool<char> peut être utilisé pour pré-allouer 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
Une autre solution consiste à passer un StringBuilder en argument au lieu d’un String. La mémoire tampon créée lors du marshaling d'un StringBuilder peut être déréférencée et modifiée par l'appelé, à condition que cela ne dépasse pas la capacité du StringBuilder. Elle peut également être initialisée sur une longueur fixe. Par exemple, si vous initialisez une mémoire tampon StringBuilder avec une capacité de N, le marshaleur fournira une mémoire tampon de (N+ 1) caractères. Le +1 tient compte du fait que la chaîne non managée a une marque de fin nulle alors que StringBuilder n’en a pas.
Remarque
En général, la transmission d’arguments StringBuilder n’est pas recommandée si vous vous souciez des performances. Pour plus d’informations, consultez Paramètres de chaîne.