Geração de origem para empacotamento personalizado
O .NET 7 introduz um novo mecanismo para personalização de como um tipo é empacotado ao usar a interoperabilidade gerada pela fonte. O gerador de origem para P/Invokes reconhece MarshalUsingAttribute e NativeMarshallingAttribute como indicadores para empacotamento personalizado de um tipo.
NativeMarshallingAttribute pode ser aplicado a um tipo para indicar o empacotamento personalizado padrão para esse tipo. O MarshalUsingAttribute pode ser aplicado a um parâmetro ou valor de retorno para indicar o empacotamento personalizado para esse uso específico do tipo, tendo precedência sobre qualquer NativeMarshallingAttribute um que possa estar no próprio tipo. Ambos os atributos esperam um Type—o tipo marshaller de ponto de entrada—que é marcado com um ou mais CustomMarshallerAttribute atributos. Cada CustomMarshallerAttribute um indica qual implementação de marshaller deve ser usada para organizar o tipo gerenciado especificado para o MarshalMode.
Implementação do Marshaller
As implementações Marshaller podem ser apátridas ou stateful. Se o tipo marshaller for uma static
classe, é considerado apátrida. Se for um tipo de valor, ele é considerado stateful e uma instância desse marshaller será usada para empacotar um parâmetro específico ou valor de retorno. Esperam-se formas diferentes para a implementação do marshaller com base no facto de um marshaller ser apátrida ou stateful e se suporta o marshalling de gerido para não gerido, não gerido para gerido, ou ambos. O SDK do .NET inclui analisadores e fixadores de código para ajudar na implementação de marshallers que estejam em conformidade com as formas necessárias.
MarshalMode
O MarshalMode especificado em a CustomMarshallerAttribute determina o suporte de empacotamento esperado e a forma para a implementação do marshaller. Todos os modos suportam implementações de marshaller sem estado. Os modos de empacotamento de elementos não suportam implementações de marshaller com estado.
MarshalMode |
Apoio esperado | Pode ser stateful |
---|---|---|
ManagedToUnmanagedIn | Conseguiu não ser gerenciado | Sim |
ManagedToUnmanagedRef | Gerenciado para não gerenciado e não gerenciado para gerenciado | Sim |
ManagedToUnmanagedOut | Não gerenciado para gerenciar | Sim |
UnmanagedToManagedIn | Não gerenciado para gerenciar | Sim |
UnmanagedToManagedRef | Gerenciado para não gerenciado e não gerenciado para gerenciado | Sim |
UnmanagedToManagedOut | Conseguiu não ser gerenciado | Sim |
ElementIn | Conseguiu não ser gerenciado | Não |
ElementRef | Gerenciado para não gerenciado e não gerenciado para gerenciado | Não |
ElementOut | Não gerenciado para gerenciar | Não |
MarshalMode.Default indica que a implementação marshaller deve ser usada para qualquer modo que ele suporte. Se uma implementação marshaller para um mais específico MarshalMode
também for especificada, ela terá precedência sobre MarshalMode.Default
.
Utilização básica
Podemos especificar NativeMarshallingAttribute em um tipo, apontando para um tipo de marshaller de ponto de entrada que é uma static
classe ou um struct
.
[NativeMarshalling(typeof(ExampleMarshaller))]
public struct Example
{
public string Message;
public int Flags;
}
ExampleMarshaller
, o tipo marshaller de ponto de entrada está marcado com CustomMarshallerAttribute, apontando para um tipo de implementação de marshaller. Neste exemplo, ExampleMarshaller
é o ponto de entrada e a implementação. Está em conformidade com as formas de marshaller esperadas para o marshaling personalizado de um valor.
[CustomMarshaller(typeof(Example), MarshalMode.Default, typeof(ExampleMarshaller))]
internal static class ExampleMarshaller
{
public static ExampleUnmanaged ConvertToUnmanaged(Example managed)
=> throw new NotImplementedException();
public static Example ConvertToManaged(ExampleUnmanaged unmanaged)
=> throw new NotImplementedException();
public static void Free(ExampleUnmanaged unmanaged)
=> throw new NotImplementedException();
internal struct ExampleUnmanaged
{
public IntPtr Message;
public int Flags;
}
}
O ExampleMarshaller
exemplo é um marshaller apátrida que implementa o suporte para marshalling de gerenciado para não gerenciado e de não gerenciado para gerenciado. A lógica de marshalling é totalmente controlada pela sua implementação de marshaller. A marcação de campos em uma struct com MarshalAsAttribute não tem efeito sobre o código gerado.
O Example
tipo pode então ser usado na geração de origem P/Invoke. No exemplo P/Invoke a seguir, ExampleMarshaller
será usado para empacotar o parâmetro de managed para unmanaged. Ele também será usado para organizar o valor de retorno de não gerenciado para 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 local de uso. No exemplo P/Invoke a seguir, ExampleMarshaller
será usado para empacotar o parâmetro de managed para unmanaged. OtherExampleMarshaller
será usado para organizar o valor de retorno de não gerenciado para gerenciado.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(OtherExampleMarshaller))]
internal static partial Example ConvertExample(Example example);
Coleções
Aplique o ContiguousCollectionMarshallerAttribute a um tipo de ponto de entrada de marshaller para indicar que é para 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 type é 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 empacotamento personalizado para um List<T>arquivo . No código a seguir, ListMarshaller
é o ponto de entrada e a implementação. Está em conformidade com as formas de marshaller esperadas para o marshaling personalizado de uma coleção.
[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)
=> throw new NotImplementedException();
public static ReadOnlySpan<T> GetManagedValuesSource(List<T> managed)
=> throw new NotImplementedException();
public static Span<TUnmanagedElement> GetUnmanagedValuesDestination(byte* unmanaged, int numElements)
=> throw new NotImplementedException();
public static List<T> AllocateContainerForManagedElements(byte* unmanaged, int length)
=> throw new NotImplementedException();
public static Span<T> GetManagedValuesDestination(List<T> managed)
=> throw new NotImplementedException();
public static ReadOnlySpan<TUnmanagedElement> GetUnmanagedValuesSource(byte* nativeValue, int numElements)
=> throw new NotImplementedException();
public static void Free(byte* unmanaged)
=> throw new NotImplementedException();
}
O ListMarshaller
exemplo é um marshaller de coleta apátrida que implementa suporte para marshalling de gerenciado para não gerenciado e de não gerenciado para gerenciado para gerenciado foro List<T>. No exemplo P/Invoke a seguir, ListMarshaller
será usado para organizar o parâmetro de managed para unmanaged e para marshal o valor de retorno de unmanaged para managed. CountElementName indica que o numValues
parâmetro deve ser usado como a contagem de elementos ao organizar o valor de retorno de não gerenciado para gerenciado.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(ListMarshaller<,>), CountElementName = "numValues")]
internal static partial void ConvertList(
[MarshalUsing(typeof(ListMarshaller<,>))] List<int> list,
out int numValues);