Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O .NET 7 introduz um novo mecanismo para a personalização de como um tipo é manipulado ao usar a interoperabilidade gerada a partir do código-fonte. O gerador de código fonte para P/Invokes reconhece MarshalUsingAttribute e NativeMarshallingAttribute como indicadores para o marshalling personalizado de um tipo.
NativeMarshallingAttribute pode ser aplicado a um tipo para indicar o marshalling personalizado padrão para esse tipo. O MarshalUsingAttribute pode ser aplicado a um parâmetro ou valor de retorno para indicar o marshalling personalizado para aquele uso específico do tipo, tendo precedência sobre qualquer NativeMarshallingAttribute que possa estar no próprio tipo. Ambos os atributos esperam um Typetipo de marshaller de ponto de entrada marcado com um ou mais CustomMarshallerAttribute atributos. Cada CustomMarshallerAttribute indica qual implementação de marshaller deve ser usada para converter o tipo gerenciado especificado para o MarshalMode especificado.
Implementação do Marshaller
As implementações do Marshaller podem ser sem estado ou com estado. Se o tipo de marshaller for uma classe static
, será considerado sem estado. Se for um tipo de valor, ele será considerado com estado e uma instância desse marshalling será usada para processar um parâmetro específico ou um valor de retorno. Diferentes formas para a implementação do marshaller são esperadas dependendo de o marshaller ser sem estado ou com estado, e se dá suporte ao marshalling de gerenciado para não-gerenciado, de não-gerenciado para gerenciado ou em ambas as direções. O SDK do .NET inclui analisadores e corretores de código para ajudar na implementação de marshallers que estão em conformidade com as formas necessárias.
MarshalMode
O MarshalMode especificado em um CustomMarshallerAttribute determina o suporte de marshalling esperado e a forma para a implementação do marshaller. Todos os modos dão suporte a implementações de marshaller sem estado. Os modos de marshalling de elementos não dão suporte a implementações de marshaller com estado.
MarshalMode |
Suporte esperado | Pode ser com estado |
---|---|---|
ManagedToUnmanagedIn | Gerenciado para não gerenciado | Sim |
ManagedToUnmanagedRef | Gerenciado para não gerenciado e não gerenciado para gerenciado | Sim |
ManagedToUnmanagedOut | Não gerenciado para gerenciado | Sim |
UnmanagedToManagedIn | Não gerenciado para gerenciado | Sim |
UnmanagedToManagedRef | Gerenciado para não gerenciado e não gerenciado para gerenciado | Sim |
UnmanagedToManagedOut | Gerenciado para não gerenciado | Sim |
ElementIn | Gerenciado para não gerenciado | Não |
ElementRef | Gerenciado para não gerenciado e não gerenciado para gerenciado | Não |
ElementOut | Não gerenciado para gerenciado | Não |
MarshalMode.Default indica que a implementação do marshaller deve ser usada para qualquer modo que ele suporta. Se uma implementação de marshaller mais específica para MarshalMode
também for especificada, ela terá precedência sobre MarshalMode.Default
.
Uso Básico
Podemos especificar NativeMarshallingAttribute em um tipo, que aponta para um tipo marshaller de ponto de entrada que seja uma classe static
ou um struct
.
[NativeMarshalling(typeof(ExampleMarshaller))]
public struct Example
{
public string Message;
public int Flags;
}
ExampleMarshaller
, o tipo de marshaller de ponto de entrada, é marcado com CustomMarshallerAttribute, apontando para uma implementação de tipo de marshaller. Neste exemplo, ExampleMarshaller
é o ponto de entrada e a implementação. Ele está em conformidade com as formas marshaller esperadas para o marshalling personalizado de um valor. Observe que ExampleMarshaller
pressupõe a codificação de cadeia de caracteres UTF-8.
[CustomMarshaller(typeof(Example), MarshalMode.Default, typeof(ExampleMarshaller))]
internal static unsafe class ExampleMarshaller
{
public static ExampleUnmanaged ConvertToUnmanaged(Example managed)
{
return new ExampleUnmanaged()
{
Message = (IntPtr)Utf8StringMarshaller.ConvertToUnmanaged(managed.Message),
Flags = managed.Flags
};
}
public static Example ConvertToManaged(ExampleUnmanaged unmanaged)
{
return new Example()
{
Message = Utf8StringMarshaller.ConvertToManaged((byte*)unmanaged.Message),
Flags = unmanaged.Flags
};
}
public static void Free(ExampleUnmanaged unmanaged)
{
Utf8StringMarshaller.Free((byte*)unmanaged.Message);
}
internal struct ExampleUnmanaged
{
public IntPtr Message;
public int Flags;
}
}
No exemplo, o ExampleMarshaller
é um marshaller sem estado que implementa suporte a marshalling de gerenciado para não gerenciado e de não gerenciado para gerenciado. A lógica de organização é totalmente controlada pela implementação do controlador. Marcar campos em uma struct com MarshalAsAttribute não tem efeito no código gerado.
Em seguida, o tipo Example
pode ser usado na geração de código-fonte P/Invoke. No exemplo P/Invoke a seguir, ExampleMarshaller
será usado para realizar o processo de marshaling do parâmetro de gerenciado para não gerenciado. Ele também será usado para realizar marshaling do valor retornado do ambiente não gerenciado para o gerenciado.
[LibraryImport("nativelib")]
internal static partial Example ConvertExample(Example example);
Para usar um marshaller diferente para um uso específico do Example
tipo, especifique MarshalUsingAttribute no site de uso. No exemplo P/Invoke a seguir, ExampleMarshaller
será usado para realizar o processo de marshaling do parâmetro de gerenciado para não gerenciado.
OtherExampleMarshaller
será usado para realizar o marshaling do valor retornado de não gerenciado para gerenciado.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(OtherExampleMarshaller))]
internal static partial Example ConvertExample(Example example);
Coleções
Aplique o ContiguousCollectionMarshallerAttribute ao tipo de ponto de entrada de marshaller para indicar que é destinado a coleções contíguas. O tipo deve ter um parâmetro de tipo a mais do que o tipo gerenciado associado. O último parâmetro de tipo é um espaço reservado e será preenchido pelo gerador de origem com o tipo não gerenciado para o tipo de elemento da coleção.
Por exemplo, você pode especificar o marshalling personalizado para um List<T>. No código a seguir, ListMarshaller
é tanto o ponto de entrada como a implementação. Ele está em conformidade com os marshaller shapes esperados para o marshal personalizado de uma coleção. Observe que é um exemplo incompleto.
[ContiguousCollectionMarshaller]
[CustomMarshaller(typeof(List<>), MarshalMode.Default, typeof(ListMarshaller<,>))]
public unsafe static class ListMarshaller<T, TUnmanagedElement> where TUnmanagedElement : unmanaged
{
public static byte* AllocateContainerForUnmanagedElements(List<T> managed, out int numElements)
{
numElements = managed.Count;
nuint collectionSizeInBytes = managed.Count * /* size of T */;
return (byte*)NativeMemory.Alloc(collectionSizeInBytes);
}
public static ReadOnlySpan<T> GetManagedValuesSource(List<T> managed)
=> CollectionsMarshal.AsSpan(managed);
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(byte* unmanaged, int numElements)
=> new Span<TUnmanagedElement>((TUnmanagedElement*)unmanaged, numElements);
public static List<T> AllocateContainerForManagedElements(byte* unmanaged, int length)
=> new List<T>(length);
public static Span<T> GetManagedValuesDestination(List<T> managed)
=> CollectionsMarshal.AsSpan(managed);
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(byte* nativeValue, int numElements)
=> new ReadOnlySpan<TUnmanagedElement>((TUnmanagedElement*)nativeValue, numElements);
public static void Free(byte* unmanaged)
=> NativeMemory.Free(unmanaged);
}
O ListMarshaller
no exemplo é um marshaller de coleção sem estado que implementa o suporte para marshalling de gerenciado para não gerenciado e de não gerenciado para gerenciado para um List<T>. No exemplo P/Invoke a seguir, ListMarshaller
será usado para realizar o marshaling do parâmetro e do valor retornado de gerenciado para não gerenciado e vice-versa.
CountElementName indica que o numValues
parâmetro deve ser usado como a contagem de elementos ao agrupar o valor retornado de não gerenciado para gerenciado.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(ListMarshaller<,>), CountElementName = "numValues")]
internal static partial List<int> ConvertList(
[MarshalUsing(typeof(ListMarshaller<,>))] List<int> list,
out int numValues);