Aracılığıyla paylaş


Özel marshalling için kaynak oluşturma

.NET 7, kaynak tarafından oluşturulan birlikte çalışma kullanılırken bir türün nasıl sıralandığına ilişkin özelleştirmeye yönelik yeni bir mekanizma sunar. P/Invokes için kaynak oluşturucu bir tür için özel düzenleme işaretçileri olarak MarshalUsingAttribute ve NativeMarshallingAttribute tanır.

NativeMarshallingAttribute bu tür için varsayılan özel sıralamayı belirtmek üzere bir türe uygulanabilir. MarshalUsingAttribute, türün özel bir kullanımına yönelik özel marshalling belirtmek için bir parametreye veya dönüş değerine uygulanabilir ve bu, türün kendisinde olabilecek herhangi bir NativeMarshallingAttribute öğesinden önceliklidir. Bu özniteliklerin her ikisi de bir veya daha fazla Type öznitelikle işaretlenmiş bir CustomMarshallerAttribute(giriş noktası marshaller türü) bekler. Her CustomMarshallerAttribute , belirtilen MarshalMode için, belirtilen yönetilen türü düzenlemek üzere hangi marshaller uygulamasının kullanılacağını gösterir.

Marshaller uygulaması

Özel marshaller uygulamaları durum bilgisi olmayan veya durum bilgisi olan uygulamalar olabilir. Marshaller türü bir static sınıfsa durum bilgisi yok olarak kabul edilir ve uygulama yöntemleri çağrılar arasında durumu izlememelidir. Bir değer türüyse durum bilgisi olduğu kabul edilir ve belirli bir parametreyi veya dönüş değerini sıralamak için bu marshaller'ın bir örneği kullanılır. Benzersiz bir örnek kullanmak, durumun marshalling ve unmarshalling işlemi boyunca korunmasını sağlar.

Marshaller şekilleri

Hazırlama oluşturucusunun özel bir marshaller türünden beklediği yöntem kümesi, marshaller şekli olarak adlandırılır. .NET Standard 2.0'da durum bilgisi olmayan statik özel marshaller türlerini desteklemek (statik arabirim yöntemlerini desteklemez) ve performansı geliştirmek için, arabirim türleri, marshaller şekillerini tanımlamak ve uygulamak için kullanılmaz. Bunun yerine, şekiller Özel marshaller şekilleri başlıklı makalede belgelenmiştir. Beklenen yöntemler (veya şekil), marshaller'ın durum bilgisi olmayan mı yoksa durum bilgisi olan mı olduğuna ve yönetilenden yönetilmeyene, yönetilmeyenden yönetilene veya her ikisine (ile CustomMarshallerAttribute.MarshalModebildirilir) kadar marshalling'i destekleyip desteklemediğine bağlıdır. .NET SDK'sı, gerekli şekillere uygun marshaller'ları uygulamaya yardımcı olmak için çözümleyicileri ve kod düzelticileri içerir.

MarshalMode

CustomMarshallerAttribute içinde belirtilen MarshalMode, marshaller uygulaması için beklenen marshalling desteğini ve yapısını belirler. Tüm modlar, durum bilgisi tutmayan marshaller uygulamalarını destekler. Eleman yönlendirme modları durumlu marshaller uygulamalarını desteklemez.

MarshalMode Beklenen destek Durumlu olabilir
ManagedToUnmanagedIn Yönetimden yönetimsizliğe Evet
ManagedToUnmanagedRef Yönetilenden yönetilmeyene ve yönetilmeyenden yönetilene Evet
ManagedToUnmanagedOut Yönetilmeyenden yönetilene geçiş Evet
UnmanagedToManagedIn Yönetilmeyenden yönetilene geçiş Evet
UnmanagedToManagedRef Yönetilenden yönetilmeyene ve yönetilmeyenden yönetilene Evet
UnmanagedToManagedOut Yönetimden yönetimsizliğe Evet
ElementIn Yönetimden yönetimsizliğe Hayı
ElementRef Yönetilenden yönetilmeyene ve yönetilmeyenden yönetilene Hayı
ElementOut Yönetilmeyenden yönetilene geçiş Hayı

Marshaller uygulamasının uyguladığı yöntemlere göre desteklenen modlara uygulandığını belirtmek için kullanın MarshalMode.Default . Daha belirli bir MarshalMode için bir marshaller belirtirseniz, bu marshaller, Default olarak işaretlenen marshaller'ın önüne geçer.

Temel kullanım

Tek bir değer düzenleme

Bir tür için özel bir marshaller oluşturmak için, gerekli marshalling yöntemlerini uygulayan bir giriş noktası marshaller türü tanımlamanız gerekir. Giriş noktası marshaller türü bir static sınıf veya structolabilir ve ile CustomMarshallerAttributeişaretlenmelidir.

Örneğin, yönetilen ve yönetilmeyen kod arasında sıralamak istediğiniz basit bir tür düşünün:

public struct Example
{
    public string Message;
    public int Flags;
}

Marshaller türünü tanımlama

Adı ExampleMarshaller olan bir tür oluşturabilirsiniz ve bu tür, CustomMarshallerAttribute ile işaretlenerek Example türü için özel marshalllama bilgileri sağlayan giriş noktası marshaller türü olduğunu belirtir. İlk argüman olan CustomMarshallerAttribute, marshaller'ın hedef aldığı yönetilen türdür. Marshaller'ın desteklediği MarshalMode, ikinci bağımsız değişkendir. Üçüncü bağımsız değişken, beklenen yapıda yöntemleri uygulayan bir tür olan marshaller türünün kendisidir.

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

ExampleMarshaller burada gösterilen, yönetilen Example türünden yerel kodun beklediği formatta bir blittable gösterime durum bilgisi olmayan sıralamayı uygular ve geri dönüştürür (ExampleUnmanaged). Free yöntemi, sıralama işlemi sırasında ayrılan yönetilmeyen kaynakları serbest bırakmak için kullanılır. Marshalling mantığı tamamen marshaller uygulaması tarafından denetlenmektedir. Yapıdaki alanları ile MarshalAsAttribute işaretlemenin oluşturulan kod üzerinde hiçbir etkisi yoktur.

Burada hem ExampleMarshaller giriş noktası türü hem de uygulama türü yer alır. Ancak gerekirse, her mod için ayrı marshaller türleri oluşturarak farklı modlar için geçiş işlemini özelleştirebilirsiniz. Aşağıdaki sınıfta olduğu gibi her mod için yeni CustomMarshallerAttribute bir ekleyin. Genellikle, bu yalnızca durum bilgisine sahip marshaller'lar için gereklidir; burada marshaller türü, çağrılar arasında durumu sürdüren bir struct türüdür. Kural gereği, uygulama türleri giriş noktası marshaller türü içinde iç içe yerleştirilmiştir.

[CustomMarshaller(typeof(Example), MarshalMode.ManagedToUnmanagedIn, typeof(ExampleMarshaller.ManagedToUnmanagedIn))]
[CustomMarshaller(typeof(Example), MarshalMode.ManagedToUnmanagedOut, typeof(ExampleMarshaller.UnmanagedToManagedOut))]
internal static class ExampleMarshaller
{
    internal struct ManagedToUnmanagedIn
    {
        public void FromManaged(TManaged managed) => throw new NotImplementedException();

        public TNative ToUnmanaged() => throw new NotImplementedException();

        public void Free() =>  throw new NotImplementedException()
    }

    internal struct UnmanagedToManagedOut
    {
        public void FromUnmanaged(TNative unmanaged) => throw new NotImplementedException();

        public TManaged ToManaged() => throw new NotImplementedException();

        public void Free() => throw new NotImplementedException();
    }
}

Kullanılacak marshaller'ı belirleme

Marshaller türünü oluşturduktan sonra, birlikte çalışma yöntemi imzasını kullanarak MarshalUsingAttribute bu marshaller'ı belirli bir parametre veya dönüş değeri için kullanmak istediğinizi belirtebilirsiniz. , MarshalUsingAttribute bu örnekte ExampleMarshallerbağımsız değişken olarak giriş noktası marshaller türünü alır.

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

Her kullanımda marshaller türünü belirtmek zorunda kalmamak için, NativeMarshallingAttribute'i doğrudan Example türüne de uygulayabilirsiniz. Bu, birlikte çalışma kaynağı oluşturmada türün tüm kullanımları için belirtilen marshaller'ın Example varsayılan olarak kullanılması gerektiğini gösterir.

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

Daha sonra Example türü, kaynağın oluşturduğu P/Invoke yöntemlerinde, marshaller türü belirtilmeden kullanılabilir. Aşağıdaki P/Invoke örneğinde, ExampleMarshaller parametresini yönetilenden yönetilmeyene sıralamak için kullanılacaktır. Ayrıca yönetilmeyenden yönetilene dönüş değerini sıralamak için de kullanılır.

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

Belirli bir parametre veya Example türündeki bir dönüş değeri için farklı bir marshaller kullanmak istediğinizde, kullanım yerinde MarshalUsingAttribute belirtin. Aşağıdaki P/Invoke örneğinde, ExampleMarshaller parametresini yönetilenden yönetilmeyene sıralamak için kullanılacaktır. OtherExampleMarshaller , yönetilmeyenden yönetilene dönüş değerini sıralamak için kullanılır.

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

Koleksiyonları hazırlama

Genel olmayan koleksiyonlar

Öğenin türü üzerinde genel olmayan koleksiyonlar için, daha önce gösterildiği gibi basit bir marshaller türü oluşturmanız gerekir.

Genel koleksiyonlar

Genel koleksiyon türü için özel bir marshaller oluşturmak için özniteliğini ContiguousCollectionMarshallerAttribute kullanabilirsiniz. Bu öznitelik, sıralayıcının diziler veya listeler gibi bitişik koleksiyonlar için olduğunu gösterir ve koleksiyon öğelerinin sıralanmasını desteklemek için marshaller'ın uygulaması gereken bir dizi yöntem sağlar. Önceden açıklanan yöntemler kullanılarak, hazırlanmış koleksiyonun öğe türü için tanımlanmış bir marshaller da olmalıdır.

Bitişik koleksiyonlar için olduğunu göstermek amacıyla ContiguousCollectionMarshallerAttribute öğesini marshaller giriş noktası türüne uygulayın. Marshaller giriş noktası türü, ilişkili yönetilen türden daha fazla tür parametresine sahip olmalıdır. Son tür parametresi bir yer tutucudur ve kaynak oluşturucu tarafından koleksiyonun öğe türü için yönetilmeyen türle doldurulur.

Örneğin, bir List<T>için özel sıralama belirtebilirsiniz. Aşağıdaki kodda hem ListMarshaller giriş noktası hem de uygulamadır. Bir koleksiyonun özel marshalllama için beklenen marshaller şekillerinden birine uygundur. (Tamamlanmamış bir örnek olduğunu unutmayın.)

[ContiguousCollectionMarshaller]
[CustomMarshaller(typeof(List<>), MarshalMode.Default, typeof(ListMarshaller<,>.DefaultMarshaller))]
public unsafe static class ListMarshaller<T, TUnmanagedElement> where TUnmanagedElement : unmanaged
{
    public static class DefaultMarshaller
    {
        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);
    }
}

Örnekteki ListMarshaller, List<T> için yönetilenden yönetilmeyene ve yönetilmeyenden yönetilene veri aktarımı desteği sağlayan durum bilgisi olmayan bir koleksiyon aktarımcısıdır. Aşağıdaki P/Invoke örneğinde, ListMarshaller yönetilen ortamdan yönetilmeyen ortama parametre koleksiyonu kapsayıcısını aktarmak ve yönetilmeyen ortamdan yönetilen ortama dönüş değeri koleksiyonu kapsayıcısını aktarmak için kullanılır. Kaynak kod oluşturucu, öğelerin parametre list'den marshaller tarafından sağlanan kapsayıcıya aktarılması için kod üretir. int Blittable olduğundan, öğelerin kendilerinin marşal edilmesi gerekmez. CountElementName etiketi, numValues parametresinin yönetilmeyenden yönetilene dönüş değerini sıralarken eleman sayısı olarak kullanılması gerektiğini gösterir.

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

Koleksiyonun eleman türü özel bir tür olduğunda, bunun için eleman dönüştürücüsünü MarshalUsingAttribute ile ek bir ElementIndirectionDepth = 1 kullanarak belirleyebilirsiniz. ListMarshaller, koleksiyon kapsayıcısını işler ve ExampleMarshaller, her öğeyi yönetilmeyen ortamdan yönetilen ortama ve tam tersi yönde yönlendirir. , ElementIndirectionDepth marshaller'ın koleksiyonun kendisinden bir düzey daha derin olan öğelerine uygulanması gerektiğini gösterir.

[LibraryImport("nativelib")]
[MarshalUsing(typeof(ListMarshaller<,>), CountElementName = nameof(numValues))]
[MarshalUsing(typeof(ExampleMarshaller), ElementIndirectionDepth = 1)]
internal static partial void ConvertList(
    [MarshalUsing(typeof(ListMarshaller<,>))]
    [MarshalUsing(typeof(ExampleMarshaller), ElementIndirectionDepth = 1)]
    List<Example> list,
    out int numValues);

Ayrıca bakınız