Поделиться через


Создание источника для пользовательского маршалинга

.NET 7 представляет новый механизм настройки того, как тип маршалируется при использовании созданного источником взаимодействия. Генератор источника для P/Invokes распознает MarshalUsingAttribute и NativeMarshallingAttribute как индикаторы для пользовательского маршалинга типа.

NativeMarshallingAttribute можно применить к типу, чтобы указать настраиваемую маршалинг по умолчанию для этого типа. Его MarshalUsingAttribute можно применить к параметру или возвращаемому значению, чтобы указать настраиваемое маршалирование для конкретного использования типа, что имеет приоритет над любым NativeMarshallingAttribute , который может быть на самом типе. Оба этих атрибута ожидают Typeтип маршаллера точки входа, помеченный одним или несколькими CustomMarshallerAttribute атрибутами. Каждый CustomMarshallerAttribute указывает, какую реализацию маршалера следует использовать для маршалирования указанного управляемого типа для указанного MarshalMode.

Реализация Маршаллера

Реализации Маршаллера могут быть без отслеживания состояния или состояния. Если тип маршаллера является классом static , он считается без отслеживания состояния. Если это тип значения, он считается отслеживанием состояния, и один экземпляр этого маршализатора будет использоваться для маршалирования определенного параметра или возвращаемого значения. Ожидается, что различные формы для реализации маршаллировщика зависят от того, является ли маршаллировщик без отслеживания состояния или поддерживает ли он маршалирование от управляемого до неуправляемого, неуправляемого или обоих. Пакет SDK для .NET включает анализаторы и средства исправления кода для реализации маршаллеров, соответствующих требуемой фигуре.

MarshalMode

Указанный MarshalMode в объекте CustomMarshallerAttribute определяет ожидаемую поддержку маршалинга и форму для реализации маршаллатора. Все режимы поддерживают реализации маршаллера без отслеживания состояния. Режимы маршаллинга элементов не поддерживают реализации маршализатора с отслеживанием состояния.

MarshalMode Ожидаемая поддержка Может быть состоянием
ManagedToUnmanagedIn Управляемый неуправляемый Да
ManagedToUnmanagedRef Удалось неуправляемый и неуправляемый для управляемого управления Да
ManagedToUnmanagedOut Неуправляемый для управляемого управления Да
UnmanagedToManagedIn Неуправляемый для управляемого управления Да
UnmanagedToManagedRef Удалось неуправляемый и неуправляемый для управляемого управления Да
UnmanagedToManagedOut Управляемый неуправляемый Да
ElementIn Управляемый неуправляемый No
ElementRef Удалось неуправляемый и неуправляемый для управляемого управления No
ElementOut Неуправляемый для управляемого управления No

MarshalMode.Default указывает, что реализация маршаллера должна использоваться для любого режима, который он поддерживает. Если также указана реализация маршаллера для более конкретного MarshalMode , она имеет приоритет над MarshalMode.Default.

Базовое использование

Мы можем указать NativeMarshallingAttribute тип, указывая на тип маршаллера точки входа, который является классом static или классом struct.

[NativeMarshalling(typeof(ExampleMarshaller))]
public struct Example
{
    public string Message;
    public int Flags;
}

ExampleMarshaller, тип маршаллатора точки входа, помечен как CustomMarshallerAttribute,указывающий на тип реализации маршаллатора. В этом примере используется как точка входа, ExampleMarshaller так и реализация. Он соответствует фигурам маршаллера, ожидаемым для пользовательского маршалинга значения.

[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;
    }
}

В ExampleMarshaller этом примере используется маршал без отслеживания состояния, который реализует поддержку маршалинга от управляемого до неуправляемого и неуправляемого. Логика маршаллинга полностью контролируется реализацией маршаллера. Маркировка полей структуры не MarshalAsAttribute влияет на созданный код.

Затем Example тип можно использовать в создании источника P/Invoke. В следующем примере ExampleMarshaller P/Invoke будет использоваться для маршалирования параметра из управляемого в неуправляемый. Он также будет использоваться для маршалирования возвращаемого значения из неуправляемого в управляемый.

[LibraryImport("nativelib")]
internal static partial Example ConvertExample(Example example);

Чтобы использовать другой маршаллировщик для конкретного Example использования типа, укажите MarshalUsingAttribute на сайте использования. В следующем примере ExampleMarshaller P/Invoke будет использоваться для маршалирования параметра из управляемого в неуправляемый. OtherExampleMarshaller будет использоваться для маршалирования возвращаемого значения из неуправляемого в управляемый.

[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(OtherExampleMarshaller))]
internal static partial Example ConvertExample(Example example);

Коллекции

ContiguousCollectionMarshallerAttribute Примените тип точки входа маршаллера, чтобы указать, что это для смежных коллекций. Тип должен иметь один параметр типа, чем связанный управляемый тип. Последний параметр типа является заполнителем и будет заполнен генератором источника с неуправляемыми типами для типа элемента коллекции.

Например, можно указать настраиваемую List<T>маршалинг для . В следующем коде используется как точка входа, ListMarshaller так и реализация. Он соответствует фигурам маршаллера, ожидаемым для пользовательского маршаллинга коллекции.

[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();
}

В ListMarshaller этом примере используется маршализатор коллекции без отслеживания состояния, который реализует поддержку маршалинга из управляемого в неуправляемый и неуправляемый для List<T>управляемого объекта. В следующем примере ListMarshaller P/Invoke будет использоваться для маршалирования параметра из управляемого в неуправляемый и маршалирования возвращаемого значения из неуправляемого в управляемый. CountElementName указывает, что numValues параметр должен использоваться в качестве количества элементов при маршале возвращаемого значения из неуправляемого в управляемый.

[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(ListMarshaller<,>), CountElementName = "numValues")]
internal static partial void ConvertList(
    [MarshalUsing(typeof(ListMarshaller<,>))] List<int> list,
    out int numValues);

См. также