.NET 7에는 소스 생성 interop을 사용할 때 형식이 마샬링되는 방식을 사용자 지정하기 위한 새로운 메커니즘이 도입되었습니다. P/Invokes의 원본 생성기는 형식의 사용자 지정 마샬링에 대한 표시기로 MarshalUsingAttribute 및 NativeMarshallingAttribute를 인식합니다.
NativeMarshallingAttribute 는 해당 형식에 대한 기본 사용자 지정 마샬링을 나타내기 위해 형식에 적용할 수 있습니다. MarshalUsingAttribute은 매개 변수나 반환 값에 적용되어 해당 형식의 특정 사용에 대한 사용자 지정 마샬링을 나타내며, 이는 형식 자체에 존재할 수 있는 NativeMarshallingAttribute보다 우선됩니다. 이 두 특성 모두 하나 이상의 Type 특성으로 표시된 진입점 마샬러 형식을 CustomMarshallerAttribute 예상합니다. 각 CustomMarshallerAttribute은 지정된 MarshalMode에 대해 지정된 관리 형식을 마샬링하도록 사용해야 하는 마샬러 구현을 나타냅니다.
마샬러 구현
마샬러 구현은 상태 비저장 또는 상태 저장일 수 있습니다. 마샬러 형식이 static
클래스인 경우, 이를 상태 비저장 형식으로 간주합니다. 값 형식인 경우 상태 저장으로 간주되며 해당 마샬러의 한 인스턴스를 사용하여 특정 매개 변수를 마샬링하거나 값을 반환합니다.
마샬러 구현의 다양한 형태는 마샬러가 상태 비저장인지 상태 저장인지 여부와 관리형에서 비관리형으로, 혹은 비관리형에서 관리형으로, 또는 양방향으로 마샬링을 지원하는지에 따라 달라집니다. .NET SDK에는 필요한 셰이프를 준수하는 마샬러를 구현하는 데 도움이 되는 분석기 및 코드 픽서가 포함되어 있습니다.
MarshalMode
MarshalMode에 지정된 CustomMarshallerAttribute는 마샬러 구현에 기대되는 데이터 변환 지원과 형태를 결정합니다. 모든 모드는 비상태 마샬러 구현을 지원합니다. 요소 마샬링 모드는 상태 저장 마샬러 구현을 지원하지 않습니다.
MarshalMode |
예상 지원 | 상태 저장 가능 |
---|---|---|
ManagedToUnmanagedIn | 관리에서 비관리로 전환 | 예 |
ManagedToUnmanagedRef | 관리됨에서 관리되지 않음으로, 관리되지 않음에서 관리됨으로 | 예 |
ManagedToUnmanagedOut | 비관리 상태에서 관리 상태로 | 예 |
UnmanagedToManagedIn | 비관리 상태에서 관리 상태로 | 예 |
UnmanagedToManagedRef | 관리됨에서 관리되지 않음으로, 관리되지 않음에서 관리됨으로 | 예 |
UnmanagedToManagedOut | 관리에서 비관리로 전환 | 예 |
ElementIn | 관리에서 비관리로 전환 | 아니오 |
ElementRef | 관리됨에서 관리되지 않음으로, 관리되지 않음에서 관리됨으로 | 아니오 |
ElementOut | 비관리 상태에서 관리 상태로 | 아니오 |
MarshalMode.Default 는 마샬러 구현이 지원하는 모든 모드에 사용되어야 임을 나타냅니다. 보다 구체적인 MarshalMode
에 대한 마샬러 구현이 지정된 경우, 이는 MarshalMode.Default
보다 우선 적용됩니다.
기본 사용법
타입에 NativeMarshallingAttribute을(를) 지정하여 진입점 마샬러 타입으로 static
클래스 또는 struct
를 가리킬 수 있습니다.
[NativeMarshalling(typeof(ExampleMarshaller))]
public struct Example
{
public string Message;
public int Flags;
}
ExampleMarshaller
에 해당하는 진입점 마샬러 유형은 CustomMarshallerAttribute 유형을 가리키도록 으로 표시됩니다. 이 예제에서는 ExampleMarshaller
가 진입점이자 구현입니다. 값의 사용자 지정 마샬링에 필요한 마샬러 셰이프를 준수합니다.
ExampleMarshaller
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;
}
}
ExampleMarshaller
는 이 예제에서 관리되는 환경에서 비관리 코드로, 비관리 코드에서 관리되는 환경으로의 마샬링을 지원하는 상태 비저장 마샬러입니다. 마샬링 논리는 마샬러 구현에 의해 완전히 제어됩니다. 구조체 MarshalAsAttribute 에 필드를 표시해도 생성된 코드에는 영향을 주지 않습니다.
Example
유형은 그런 다음 P/Invoke 원본 생성에서 사용할 수 있습니다. 다음 P/Invoke 예제 ExampleMarshaller
에서는 매개 변수를 관리되는 매개 변수에서 관리되지 않는 매개 변수로 마샬링하는 데 사용됩니다. 또한 반환 값을 관리되지 않는 값에서 관리되는 값으로 마샬링하는 데도 사용됩니다.
[LibraryImport("nativelib")]
internal static partial Example ConvertExample(Example example);
다른 마샬러를 특정 형식 사용에 적용하려면 사용 위치에서 Example
을 지정하십시오. 다음 P/Invoke 예제 ExampleMarshaller
에서는 매개 변수를 관리되는 매개 변수에서 관리되지 않는 매개 변수로 마샬링하는 데 사용됩니다.
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)
{
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);
}
예제의 ListMarshaller
는 List<T>에 대해 관리되는 환경에서 관리되지 않는 환경으로 마샬링하고, 관리되지 않는 환경에서 관리되는 환경으로 마샬링하는 지원을 구현하는 상태 비저장 컬렉션 마샬러입니다. 다음 P/Invoke 예제 ListMarshaller
에서는 매개 변수를 관리되는 매개 변수에서 관리되지 않는 매개 변수로 마샬링하고 반환 값을 관리되지 않는 값에서 관리되는 값으로 마샬링하는 데 사용됩니다.
CountElementName는 numValues
매개 변수를 관리되지 않는 값에서 관리되는 값으로 반환 값을 마샬링할 때 요소 수로 사용해야 함을 나타냅니다.
[LibraryImport("nativelib")]
[return: MarshalUsing(typeof(ListMarshaller<,>), CountElementName = "numValues")]
internal static partial List<int> ConvertList(
[MarshalUsing(typeof(ListMarshaller<,>))] List<int> list,
out int numValues);
참고하십시오
.NET