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.
Le marshaling est le processus de transformation des types lorsqu’ils doivent passer d’un code managé à un code natif.
Le marshaling est nécessaire, car les types dans le code managé et non managé sont différents. Dans le code managé, par exemple, vous avez un string, tandis que les chaînes non managées peuvent être l’encodage .NET string (UTF-16), l’encodage de page de codes ANSI, UTF-8, se terminant par null, ASCII, etc. Par défaut, le sous-système P/Invoke tente de faire la bonne chose en fonction du comportement par défaut, décrit dans cet article. Toutefois, pour les situations où vous avez besoin d’un contrôle supplémentaire, vous pouvez utiliser l’attribut MarshalAs pour spécifier le type attendu du côté non managé. Par exemple, si vous souhaitez que la chaîne soit envoyée en tant que chaîne UTF-8 terminée par null, vous pouvez le faire comme suit :
[LibraryImport("somenativelibrary.dll")]
static extern int MethodA([MarshalAs(UnmanagedType.LPUTF8Str)] string parameter);
// or
[LibraryImport("somenativelibrary.dll", StringMarshalling = StringMarshalling.Utf8)]
static extern int MethodB(string parameter);
Si vous appliquez l’attribut System.Runtime.CompilerServices.DisableRuntimeMarshallingAttribute à l’assembly, les règles de la section suivante ne s’appliquent pas. Pour plus d’informations sur la façon dont les valeurs .NET sont exposées au code natif lorsque cet attribut est appliqué, consultez le marshaling du runtime désactivé.
Règles par défaut de marshaling des types courants
En règle générale, le runtime tente de prendre la bonne décision en matière de marshaling, c’est-à-dire celle qui demande le moins de travail de votre part. Les tableaux suivants décrivent comment chaque type est marshalé par défaut lorsqu’il est utilisé dans un paramètre ou un champ. Les types entiers et caractères de largeur fixe C99/C++11 sont utilisés pour s’assurer que le tableau suivant est correct pour toutes les plateformes. Vous pouvez utiliser n’importe quel type natif qui a les mêmes exigences d’alignement et de taille que ces types.
La première table décrit les correspondances de différents types pour lesquels le marshaling P/Invoke et le marshaling des champs sont identiques.
| Mot clé C# | Type .NET | Type natif |
|---|---|---|
byte |
System.Byte |
uint8_t |
sbyte |
System.SByte |
int8_t |
short |
System.Int16 |
int16_t |
ushort |
System.UInt16 |
uint16_t |
int |
System.Int32 |
int32_t |
uint |
System.UInt32 |
uint32_t |
long |
System.Int64 |
int64_t |
ulong |
System.UInt64 |
uint64_t |
char |
System.Char |
Soit char soit char16_t selon l’encodage du P/Invoke ou de la structure. Consultez la documentation de l’ensemble de caractères. |
System.Char |
Soit char* soit char16_t* selon l’encodage du P/Invoke ou de la structure. Consultez la documentation de l’ensemble de caractères. |
|
nint |
System.IntPtr |
intptr_t |
nuint |
System.UIntPtr |
uintptr_t |
Types de pointeur .NET (par exemple void*) |
void* |
|
Type dérivé de System.Runtime.InteropServices.SafeHandle |
void* |
|
Type dérivé de System.Runtime.InteropServices.CriticalHandle |
void* |
|
bool |
System.Boolean |
Type BOOL Win32 |
decimal |
System.Decimal |
Struct DECIMAL COM |
| Délégué .NET | Pointeur de fonction natif | |
System.DateTime |
Type DATE Win32 |
|
System.Guid |
Type GUID Win32 |
Certaines catégories ont des valeurs par défaut différentes pour le marshaling comme paramètre ou comme structure.
| Type .NET | Type natif (paramètre) | Type natif (champ) |
|---|---|---|
| Tableau .NET | Pointeur vers le début d’un tableau de représentations natives des éléments du tableau | Non autorisé sans [MarshalAs] attribut |
Classe avec LayoutKind de Sequential ou de Explicit |
Pointeur vers la représentation native de la classe | Représentation native de la classe |
Le tableau suivant inclut les règles de marshaling par défaut qui sont uniquement pour Windows. Sur les autres plateformes, il n’est pas possible de marshaler ces types.
| Type .NET | Type natif (paramètre) | Type natif (champ) |
|---|---|---|
System.Object |
VARIANT |
IUnknown* |
System.Array |
Interface COM | Non autorisé sans [MarshalAs] attribut |
System.ArgIterator |
va_list |
Non autorisé |
System.Collections.IEnumerator |
IEnumVARIANT* |
Non autorisé |
System.Collections.IEnumerable |
IDispatch* |
Non autorisé |
System.DateTimeOffset |
int64_t représentant le nombre de cycles depuis le 1er janvier 1601 à minuit |
int64_t représentant le nombre de cycles depuis le 1er janvier 1601 à minuit |
Certains types peuvent uniquement être marshalés en tant que paramètres et non en tant que champs. Ces types sont répertoriés dans le tableau suivant :
| Type .NET | Type natif (paramètre uniquement) |
|---|---|
System.Text.StringBuilder |
char* ou char16_t* selon le CharSet du P/Invoke. Consultez la documentation sur les ensembles de caractères. |
System.ArgIterator |
va_list (sur Windows x86/x64/arm64 uniquement) |
System.Runtime.InteropServices.ArrayWithOffset |
void* |
System.Runtime.InteropServices.HandleRef |
void* |
Si ces valeurs par défaut ne font pas exactement ce que vous voulez, vous pouvez personnaliser la façon dont les paramètres sont marshalés. L’article de marshaling de paramètres vous guide tout au long de la façon dont différents types de paramètres sont marshalés.
Marshaling par défaut dans les scénarios COM
Lorsque vous appelez des méthodes sur des objets COM dans .NET, le runtime .NET modifie les règles de marshaling par défaut pour qu’elles correspondent à la sémantique COM courante. Le tableau suivant répertorie les règles que les runtimes .NET utilisent dans les scénarios COM :
| Type .NET | Type natif (appels de méthode COM) |
|---|---|
System.Boolean |
VARIANT_BOOL |
StringBuilder |
LPWSTR |
System.String |
BSTR |
| Types délégués |
_Delegate* dans .NET Framework. Non autorisé dans .NET Core et .NET 5+. |
System.Drawing.Color |
OLECOLOR |
| Tableau .NET | SAFEARRAY |
System.String[] |
SAFEARRAY sur BSTR |
Marshaling de classes et de structures
Un autre aspect du marshaling de types consiste à passer une structure à une méthode non managée. Par exemple, certaines des méthodes non managées nécessitent un struct en tant que paramètre. Dans ces cas, vous devez créer un struct correspondant ou une classe dans une partie gérée du monde pour l’utiliser comme paramètre. Toutefois, la définition de la classe n’est pas suffisante, vous devez également indiquer au marshaller comment mapper les champs de la classe au struct non managé. Ici, l’attribut StructLayout devient utile.
using System;
using System.Runtime.InteropServices;
Win32Interop.GetSystemTime(out Win32Interop.SystemTime systemTime);
Console.WriteLine(systemTime.Year);
internal static partial class Win32Interop
{
[LibraryImport("kernel32.dll")]
internal static partial void GetSystemTime(out SystemTime systemTime);
[StructLayout(LayoutKind.Sequential)]
internal ref struct SystemTime
{
public ushort Year;
public ushort Month;
public ushort DayOfWeek;
public ushort Day;
public ushort Hour;
public ushort Minute;
public ushort Second;
public ushort Millisecond;
}
}
Le code précédent montre un exemple simple d'appel d'une fonction GetSystemTime(). La partie intéressante se trouve à la ligne 13. L’attribut spécifie que les champs de la classe doivent être mappés séquentiellement au struct de l’autre côté (non managé). Cela signifie que le nommage des champs n’est pas important, seul son ordre est important, car il doit correspondre au struct non managé, illustré dans l’exemple suivant :
typedef struct _SYSTEMTIME {
WORD wYear;
WORD wMonth;
WORD wDayOfWeek;
WORD wDay;
WORD wHour;
WORD wMinute;
WORD wSecond;
WORD wMilliseconds;
} SYSTEMTIME, *PSYSTEMTIME, *LPSYSTEMTIME;
Il est possible que le marshaling par défaut de votre structure ne vous convienne pas. L’article Personnalisation de l'assemblage de structure vous apprend à personnaliser la manière dont votre structure est assemblée.