Partager via


Formes de marshaleur personnalisé

Cet article décrit les différentes « formes » des marshaleurs personnalisés utilisables avec le générateur de source d’interopérabilité .NET.

Marshalleurs de valeurs

Cette section décrit les formes des marshaleurs personnalisés utilisables par le générateur de source d’interopérabilité .NET pour le marshaling des types de valeurs entre le code managé et le code non managé.

Code managé vers code non managé sans état

Avec cette forme, le code généré appelle ConvertToUnmanaged pour transférer une valeur vers le code natif, ou GetPinnableReference lorsque cela est applicable. Le code généré appelle Free le cas échéant, afin de permettre au marshaleur de libérer toutes les ressources non managées associées au type managé à la fin de sa durée de vie.

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToNative))]
static class TMarshaller
{
    public static class ManagedToNative
    {
        /// <summary>
        /// Converts a managed type to an unmanaged representation. May throw exceptions.
        /// </summary>
        public static TNative ConvertToUnmanaged(TManaged managed);

        /// <summary>
        /// Optional.
        /// Returns a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// </summary>
        public static ref TNativeDereferenced GetPinnableReference(TManaged managed);

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the marshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Code managé vers code non managé sans état avec mémoire tampon allouée par l’appelant

Avec cette forme, le générateur alloue une zone tampon de la taille spécifiée et la transmet à la méthode ConvertToUnmanaged pour transférer une valeur au code natif. Le code généré gère la durée de vie de cette mémoire tampon.

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToNative))]
static class TMarshaller
{
    public static class ManagedToNative
    {
        /// <summary>
        /// The size of the buffer that will be allocated by the generator.
        /// </summary>
        public static int BufferSize { get; }

        /// <summary>
        /// Converts a managed type to an unmanaged representation using a caller-allocated buffer.
        /// </summary>
        public static TNative ConvertToUnmanaged(TManaged managed, Span<byte> callerAllocatedBuffer);

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the marshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Code non managé vers code managé sans état

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedIn, typeof(NativeToManaged))]
static class TMarshaller
{
    public static class NativeToManaged
    {
        /// <summary>
        /// Converts an unmanaged representation to a managed type. May throw exceptions.
        /// </summary>
        public static TManaged ConvertToManaged(TNative unmanaged);

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the marshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Code non managé vers code managé sans état avec unmarshaling garanti

Cette forme demande au générateur d’émettre le code d’unmarshaling de sorte à garantir que l’unmarshaling sera appelé, même si un marshaleur précédent génère une exception. Cela est utile pour les types qui doivent être nettoyés ou finalisés indépendamment du succès des opérations précédentes.

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedIn, typeof(NativeToManaged))]
static class TMarshaller
{
    public static class NativeToManaged
    {
        /// <summary>
        /// Converts an unmanaged representation to a managed type.
        /// Should not throw exceptions.
        /// </summary>
        public static TManaged ConvertToManagedFinally(TNative unmanaged);

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the marshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Bidirectionnel sans état

Cette forme permet les conversions du code managé vers le code non managé et du code non managé vers le code managé, avec un marshaleur sans état. Le générateur utilise la méthode ConvertToUnmanaged pour les conversions de géré à non géré et la méthode ConvertToManaged pour les conversions de non géré à géré.

[CustomMarshaller(typeof(TManaged<,,,...>), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))]
[CustomMarshaller(typeof(TManaged<,,,...>), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))]
[CustomMarshaller(typeof(TManaged<,,,...>), MarshalMode.ElementRef, typeof(Bidirectional))]
static class TMarshaller<T, U, V...>
{
    public static class Bidirectional
    {
        // Include members from each of the following:
        // - One Stateless Managed->Unmanaged Value shape
        // - One Stateless Unmanaged->Managed Value shape
    }
}

Code managé vers code non managé avec état

Cette forme permet le marshaling avec état du code managé vers le code non managé. Le code généré utilisera une instance unique de marshaleur pour chaque paramètre, de sorte que le marshaleur puisse maintenir l’état pendant le marshaling.

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToNative))]
static class TMarshaller
{
    public struct ManagedToNative // Can be ref struct
    {
        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public ManagedToNative();

        /// <summary>
        /// Takes a managed type to be converted to an unmanaged representation in ToUnmanaged or GetPinnableReference.
        /// </summary>
        public void FromManaged(TManaged managed);

        /// <summary>
        /// Optional.
        /// Returns a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, this method is preferred to ToUnmanaged for marshalling.
        /// </summary>
        public ref TIgnored GetPinnableReference();

        /// <summary>
        /// Optional.
        /// Returns a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, only this method is called for the marshalling step.
        /// May throw exceptions.
        /// </summary>
        public static ref TOther GetPinnableReference(TManaged managed);

        /// <summary>
        /// Converts the managed type to an unmanaged representation.
        /// May throw exceptions.
        /// </summary>
        public TNative ToUnmanaged();

        /// <summary>
        /// Optional.
        /// In managed to unmanaged stubs, this method is called after call to the unmanaged code.
        /// Must not throw exceptions.
        /// </summary>
        public void OnInvoked();

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the marshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public void Free();
    }
}

Code managé vers code non managé avec état avec mémoire tampon allouée par l’appelant

Cette forme permet le marshaling avec état du code managé vers le code non managé, le générateur allouant une mémoire tampon de la taille spécifiée et la transmettant à la méthode FromManaged. Le code généré utilisera une instance unique de marshaleur pour chaque paramètre, de sorte que le marshaleur puisse maintenir l’état pendant le marshaling.

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToNative))]
static class TMarshaller
{
    public struct ManagedToNative // Can be ref struct
    {
        /// <summary>
        /// The length of the buffer that will be allocated by the generator.
        /// </summary>
        public static int BufferSize { get; }

        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public ManagedToNative();

        /// <summary>
        /// Takes a managed type to be converted to an unmanaged representation in ToUnmanaged or GetPinnableReference.
        /// May throw exceptions.
        /// </summary>
        public void FromManaged(TManaged managed, Span<TBuffer> buffer);

        /// <summary>
        /// Optional.
        /// Returns a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, this method is preferred to ToUnmanaged for marshalling.
        /// </summary>
        public ref TIgnored GetPinnableReference();

        /// <summary>
        /// Optional.
        /// Returns a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, only this method is called for the marshalling step.
        /// May throw exceptions.
        public static ref TOther GetPinnableReference(TManaged managed);

        /// <summary>
        /// Returns the unmanaged representation of the managed value.
        /// May throw exceptions.
        /// </summary>
        public TNative ToUnmanaged();

        /// <summary>
        /// Optional.
        /// In managed to unmanaged stubs, this method is called after call to the unmanaged code.
        /// Must not throw exceptions.
        /// </summary>
        public void OnInvoked();

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the marshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public void Free();
    }
}

Code non managé vers code managé avec état

Cette forme permet l’unmarshaling avec état du code non managé vers le code managé. Le code généré utilisera une instance unique pour chaque paramètre, de sorte que le struct puisse maintenir l’état pendant l’unmarshaling.

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedIn, typeof(NativeToManaged))]
static class TMarshaller
{
    public struct NativeToManaged // Can be ref struct
    {
        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public NativeToManaged();

        /// <summary>
        /// Takes an unmanaged representation to be converted to a managed type in ToManaged.
        /// Should not throw exceptions.
        /// </summary>
        public void FromUnmanaged(TNative unmanaged);

        /// <summary>
        /// Returns the managed value representation of the unmanaged value.
        /// May throw exceptions.
        /// </summary>
        public TManaged ToManaged();

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the unmarshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public void Free();
    }
}

Code non managé vers code managé avec état avec unmarshaling garanti

Cette forme permet l’unmarshaling avec état du code non géré vers le code géré, le générateur s’assurant que la méthode ToManagedFinally est appelée même si un marshaleur précédent génère une exception. Cela est utile pour les types qui doivent être nettoyés ou finalisés indépendamment du succès des opérations précédentes.

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedIn, typeof(NativeToManaged))]
static class TMarshaller
{
    public struct NativeToManaged // Can be ref struct
    {
        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public NativeToManaged();

        /// <summary>
        /// Takes an unmanaged representation to be converted to a managed type in ToManagedFinally.
        /// Should not throw exceptions.
        /// </summary>
        public void FromUnmanaged(TNative unmanaged);

        /// <summary>
        /// Returns the managed value representation of the unmanaged value.
        /// Should not throw exceptions.
        /// </summary>
        public TManaged ToManagedFinally();

        /// <summary>
        /// Optional.
        /// Frees any unmanaged resources associated with the unmarshalling of the managed type.
        /// Must not throw exceptions.
        /// </summary>
        public void Free();
    }
}

Bidirectionnel avec état

[CustomMarshaller(typeof(TManaged), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))]
[CustomMarshaller(typeof(TManaged), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))]
static class TMarshaller
{
    public struct Bidirectional // Can be ref struct
    {
        // Include members from each of the following:
        // - One Stateful Managed->Unmanaged Value shape
        // - One Stateful Unmanaged->Managed Value shape
    }
}

Marshaleurs de collection

Cette section décrit les configurations des marshallers personnalisés de collections pris en charge par le générateur de code source .NET Interop pour le transfert de données entre le code managé et non managé. Les marshaleurs de collections contiguës sont utilisés pour les collections représentées sous la forme d’un bloc de mémoire contigu dans leur représentation non managée, comme des tableaux ou des listes.

Lorsque le type de collection en cours de marshaling est un type générique basé sur le type de ses éléments (par exemple, List<T>), le type de marshaller personnalisé comporte généralement deux paramètres génériques : un pour le type d’élément managé et un pour le type d’élément non managé. Dans les cas où la collection n’est pas générique par rapport à son type d’élément, par exemple StringCollection, le marshaller peut avoir un seul paramètre générique pour le type d’élément non géré. Les exemples de formes de marshaleur illustrent un marshaleur pour une collection non générique TCollection avec des éléments de type TManagedElement. Comme les éléments peuvent être marshalés à l’aide de différents marshaleurs de valeur, elle doit être générique sur le type d’élément non managé, TUnmanagedElement. La représentation native de la collection est appelée TNative (en général, c'est *TUnmanagedElement).

Code managé vers code non managé sans état

Avec cette forme, le générateur utilise des méthodes statiques pour convertir une collection gérée en mémoire non gérée. Le marshaller doit fournir des méthodes pour allouer le conteneur non managé, accéder aux éléments managés et non managés, et éventuellement libérer des ressources non managées.

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TCollection), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TCollection), MarshalMode.ElementIn, typeof(ManagedToNative))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public static class ManagedToNative
    {
        /// <summary>
        /// Gets the uninitialized unmanaged container for the elements and assigns the number of elements to numElements.
        /// The return value is later passed to GetUnmanagedValuesDestination.
        /// </summary>
        public static TNative AllocateContainerForUnmanagedElements(TCollection managed, out int numElements);

        /// <summary>
        /// Gets a span of managed elements in the collection.
        /// The elements in this span are marshalled by the element marshaller and put into the unmanaged container.
        /// </summary>
        public static ReadOnlySpan<TManagedElement> GetManagedValuesSource(TCollection managed);

        /// <summary>
        /// Gets a span of unmanaged elements from the TNative container.
        /// The elements in this span are filled with the marshalled values from the managed collection.
        /// </summary>
        public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TNative unmanaged, int numElements);

        /// <summary>
        /// Optional. Returns a pinnable reference to the unmanaged representations of the elements.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// </summary>
        public static ref TOther GetPinnableReference(TManaged managed);

        /// <summary>
        /// Optional. Frees unmanaged resources.
        /// Should not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Code managé vers code non managé sans état avec mémoire tampon allouée par l’appelant

Le générateur alloue une mémoire tampon et la transmet au marshaleur pour une utilisation pendant le marshaling.

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TCollection), MarshalMode.ElementIn, typeof(ManagedToNative))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public static class ManagedToNative
    {
        /// <summary>
        /// The length of the buffer that will be allocated by the generated code and passed to AllocateContainerForUnmanagedElements.
        /// </summary>
        public static int BufferSize { get; }

        /// <summary>
        /// Creates the unmanaged container using a caller-allocated buffer, and assigns the number of elements in the collection to numElements.
        /// The return value is later passed to GetUnmanagedValuesDestination.
        /// </summary>
        public static TNative AllocateContainerForUnmanagedElements(TCollection managed, Span<TBuffer> buffer, out int numElements);

        /// <summary>
        /// Gets a span of managed elements in the collection.
        /// The elements in this span are marshalled by the element marshaller and put into the unmanaged container.
        /// </summary>
        public static ReadOnlySpan<TManagedElement> GetManagedValuesSource(TCollection managed);

        /// <summary>
        /// Gets a span of unmanaged elements from the TNative container.
        /// The elements in this span are filled with the marshalled values from the managed collection.
        /// </summary>
        public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(TNative unmanaged, int numElements);

        /// <summary>
        /// Optional. Returns a pinnable reference to the unmanaged representations of the elements.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// </summary>
        public static ref TOther GetPinnableReference(TManaged managed);

        /// <summary>
        /// Optional. Frees unmanaged resources.
        /// Should not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

D'un système sans état non géré à un système géré

Cette forme marshale de la mémoire non managée vers une collection managée.

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[CustomMarshaller(typeof(TCollection), MarshalMode.UnmanagedToManagedIn, typeof(NativeToManaged))]
[CustomMarshaller(typeof(TCollection), MarshalMode.ElementOut, typeof(NativeToManaged))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public static class NativeToManaged
    {
        /// <summary>
        /// Allocates a new collection for unmarshalling.
        /// May throw exceptions.
        /// </summary>
        public static TCollection AllocateContainerForManagedElements(TNative unmanaged, int numElements);

        /// <summary>
        /// Gets the destination span that unmarshalled managed elements will be written to.
        /// May throw exceptions.
        /// </summary>
        public static Span<TManagedElement> GetManagedValuesDestination(TCollection managed);

        /// <summary>
        /// Gets the source span of unmanaged elements to unmarshal from.
        /// May throw exceptions.
        /// </summary>
        public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TNative unmanaged, int numElements);

        /// <summary>
        /// Optional. Frees unmanaged resources.
        /// Should not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Code non managé vers code managé sans état avec unmarshaling garanti

Le générateur garantit que l’unmarshaling se produit même si un marshaleur précédent génère une exception.

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public static class NativeToManaged
    {
        /// <summary>
        /// Allocates the managed container for the elements.
        /// Should not throw exceptions other than OutOfMemoryException.
        /// </summary>
        public static TCollection AllocateContainerForManagedElementsFinally(TNative unmanaged, int numElements);

        /// <summary>
        /// Gets the destination span that unmarshalled managed elements will be written to.
        /// May throw exceptions.
        /// </summary>
        public static Span<TManagedElement> GetManagedValuesDestination(TCollection managed);

        /// <summary>
        /// Gets the source span of unmanaged elements to unmarshal from.
        /// May throw exceptions.
        /// </summary>
        public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(TNative unmanaged, int numElements);

        /// <summary>
        /// Optional. Frees unmanaged resources.
        /// Should not throw exceptions.
        /// </summary>
        public static void Free(TNative unmanaged);
    }
}

Bidirectionnel sans état

Cette forme prend en charge les conversions du code managé vers le code non managé et du code non managé vers le code managé.

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))]
[CustomMarshaller(typeof(TCollection), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))]
[CustomMarshaller(typeof(TCollection), MarshalMode.ElementRef, typeof(Bidirectional))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public static class Bidirectional
    {
        // Include members from each of the following:
        // - One Stateless Managed->Unmanaged Linear Collection shape
        // - One Stateless Unmanaged->Managed Linear Collection shape
    }
}

Code managé vers code non managé avec état

Cette forme permet au marshaleur de maintenir l’état tout au long du processus de marshaling.

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[CustomMarshaller(typeof(TCollection), MarshalMode.UnmanagedToManagedOut, typeof(ManagedToNative))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public struct ManagedToNative // Can be ref struct
    {
        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public ManagedToNative(); // Optional, can throw exceptions.

        /// <summary>
        /// Takes a managed collection to be converted to an unmanaged representation in ToUnmanaged or GetPinnableReference.
        /// </summary>
        public void FromManaged(TCollection collection); // Can throw exceptions.

        /// <summary>
        /// Gets the source span of managed elements to marshal.
        /// May throw exceptions.
        /// </summary>
        public ReadOnlySpan<TManagedElement> GetManagedValuesSource(); // Can throw exceptions.

        /// <summary>
        /// Gets the destination span of unmanaged elements to marshal to.
        /// May throw exceptions.
        /// </summary>
        public Span<TUnmanagedElement> GetUnmanagedValuesDestination(); // Can throw exceptions.

        /// <summary>
        /// Optional.
        /// Gets a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, this method is preferred to ToUnmanaged for marshalling.
        /// May throw exceptions.
        /// </summary>
        public ref TOther GetPinnableReference(); // Optional. Can throw exceptions.

        /// <summary>
        /// Optional.
        /// Gets a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, this method is preferred to the instance version and ToUnmanaged for marshalling.
        /// May throw exceptions.
        /// </summary>
        public static ref TOther GetPinnableReference(TCollection collection); // Optional. Can throw exceptions. Result pinnned and passed to Invoke.

        /// <summary>
        /// Converts the managed collection to an unmanaged representation.
        /// May throw exceptions.
        /// </summary>
        public TNative ToUnmanaged(); // Can throw exceptions.

        /// <summary>
        /// Optional.
        /// In managed to unmanaged stubs, this method is called after call to the unmanaged code.
        /// Must not throw exceptions.
        /// </summary>
        public void OnInvoked(); // Optional. Should not throw exceptions.
    }
}

Code managé vers code non managé avec état avec mémoire tampon allouée par l’appelant

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedIn, typeof(ManagedToNative))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public struct ManagedToNative // Can be ref struct
    {
        /// <summary>
        /// The length of the buffer that will be allocated by the generated code and passed to FromManaged.
        /// </summary>
        public static int BufferSize { get; }

        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public ManagedToNative();

        /// <summary>
        /// Takes a managed collection to be converted to an unmanaged representation in ToUnmanaged or GetPinnableReference.
        /// The caller-allocated buffer is passed to this method.
        /// </summary>
        public void FromManaged(TCollection collection, Span<TBuffer> buffer);

        /// <summary>
        /// Gets the source span of managed elements to marshal.
        /// May throw exceptions.
        /// </summary>
        public ReadOnlySpan<TManagedElement> GetManagedValuesSource();

        /// <summary>
        /// Gets the destination span of unmanaged elements to marshal to.
        /// May throw exceptions.
        /// </summary>
        public Span<TUnmanagedElement> GetUnmanagedValuesDestination();

        /// <summary>
        /// Optional.
        /// Gets a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, this method is preferred to ToUnmanaged for marshalling.
        /// May throw exceptions.
        /// </summary>
        public ref TOther GetPinnableReference();

        /// <summary>
        /// Optional.
        /// Gets a pinnable reference to the unmanaged representation.
        /// The reference will be pinned and passed as a pointer to the native code.
        /// TOther should be a dereferenced type of TNative.
        /// For example, if TNative is `int*`, then TOther should be `int`.
        /// When applicable, this method is preferred to the instance GetPinnableReference and ToUnmanaged for marshalling.
        /// May throw exceptions.
        /// </summary>
        public static ref TOther GetPinnableReference(TCollection collection);

        /// <summary>
        /// Converts the managed collection to an unmanaged representation.
        /// May throw exceptions.
        /// </summary>
        public TNative ToUnmanaged();

        /// <summary>
        /// Optional.
        /// In managed to unmanaged stubs, this method is called after call to the unmanaged code.
        /// Must not throw exceptions.
        /// </summary>
        public void OnInvoked();
    }
}

Code non managé vers code managé avec état

Cette forme permet au marshaleur de maintenir l’état tout au long du processus d’unmarshaling.

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[CustomMarshaller(typeof(TCollection), MarshalMode.UnmanagedToManagedIn, typeof(NativeToManaged))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public struct NativeToManaged // Can be ref struct
    {
        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public NativeToManaged();

        /// <summary>
        /// Takes an unmanaged collection to be converted to a managed representation in ToManaged.
        /// Should not throw exceptions.
        /// </summary>
        public void FromUnmanaged(TNative value);

        /// <summary>
        /// Gets the source span of unmanaged elements to unmarshal from.
        /// May throw exceptions.
        /// </summary>
        public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int numElements);

        /// <summary>
        /// Gets the destination span that unmarshalled managed elements will be written to.
        /// May throw exceptions.
        /// </summary>
        public Span<TManagedElement> GetManagedValuesDestination(int numElements);

        /// <summary>
        /// Returns the managed value representation of the unmanaged value.
        /// May throw exceptions.
        /// </summary>
        public TCollection ToManaged();

        /// <summary>
        /// Optional. Should not throw exceptions.
        /// </summary>
        public void Free();
    }
}

Code non managé vers code managé avec état avec unmarshaling garanti

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedOut, typeof(NativeToManaged))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public struct NativeToManaged // Can be ref struct
    {
        /// <summary>
        /// Optional constructor.
        /// May throw exceptions.
        /// </summary>
        public NativeToManaged(); // Optional, can throw exceptions.

        /// <summary>
        /// Takes an unmanaged collection to be converted to a managed representation in ToManagedFinally.
        /// Should not throw exceptions.
        /// </summary>
        public void FromUnmanaged(TNative value);

        /// <summary>
        /// Gets the source span of unmanaged elements to unmarshal from.
        /// May throw exceptions.
        /// </summary>
        public ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(int numElements);

        /// <summary>
        /// Gets the destination span that unmarshalled managed elements will be written to.
        /// May throw exceptions.
        /// </summary>
        public Span<TManagedElement> GetManagedValuesDestination(int numElements);

        /// <summary>
        /// Returns the managed value representation of the unmanaged value.
        /// Should not throw exceptions.
        /// </summary>
        public TCollection ToManagedFinally();

        /// <summary>
        /// Optional. Should not throw exceptions.
        /// </summary>
        public void Free();
    }
}

Bidirectionnel avec état

[CustomMarshaller(typeof(TCollection), MarshalMode.ManagedToUnmanagedRef, typeof(Bidirectional))]
[CustomMarshaller(typeof(TCollection), MarshalMode.UnmanagedToManagedRef, typeof(Bidirectional))]
[ContiguousCollectionMarshaller]
static class TMarshaller<TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public struct Bidirectional // Can be ref struct
    {
        // Include members from each of the following:
        // - One Stateful Managed->Unmanaged Contiguous Collection shape
        // - One Stateful Unmanaged->Managed Contiguous Collection shape
    }
}