Description
Background and motivation
For source-generated p/invokes, #46838 and #66121 were recently approved to support custom marshalling via types marked with System.Runtime.InteropServices.CustomTypeMarshallerAttribute
and conforming to a specific shape. This issue proposes adding implementations of marshallers that the p/invoke source generator would be able use.
API Proposal
Strings:
[CLSCompliant(false)]
[CustomTypeMarshaller(typeof(string), BufferSize = 0x100,
Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling )]
public unsafe ref struct AnsiStringMarshaller
{
public AnsiStringMarshaller(string? str);
public AnsiStringMarshaller(string? str, Span<byte> buffer);
public byte* ToNativeValue();
public void FromNativeValue(byte* value);
public string? ToManaged();
public void FreeNative();
}
[CLSCompliant(false)]
[CustomTypeMarshaller(typeof(string), BufferSize = 0x100,
Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling )]
public unsafe ref struct Utf8StringMarshaller
{
public Utf8StringMarshaller(string? str);
public Utf8StringMarshaller(string? str, Span<byte> buffer);
public byte* ToNativeValue();
public void FromNativeValue(byte* value);
public string? ToManaged();
public void FreeNative();
}
[CLSCompliant(false)]
[CustomTypeMarshaller(typeof(string), BufferSize = 0x100,
Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling)]
public unsafe ref struct Utf16StringMarshaller
{
public Utf16StringMarshaller(string? str);
public Utf16StringMarshaller(string? str, Span<ushort> buffer);
public ref ushort GetPinnableReference();
public ushort* ToNativeValue();
public void FromNativeValue(ushort* value);
public string? ToManaged();
public void FreeNative();
}
Arrays:
[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder[]),
CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200,
Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling)]
public unsafe ref struct ArrayMarshaller<T>
{
public ArrayMarshaller(int sizeOfNativeElement);
public ArrayMarshaller(T[]? array, int sizeOfNativeElement);
public ArrayMarshaller(T[]? array, Span<byte> buffer, int sizeOfNativeElement);
public ReadOnlySpan<T> GetManagedValuesSource();
public Span<T> GetManagedValuesDestination(int length);
public Span<byte> GetNativeValuesDestination();
public ReadOnlySpan<byte> GetNativeValuesSource(int length);
public ref byte GetPinnableReference();
public byte* ToNativeValue();
public void FromNativeValue(byte* value);
public T[]? ToManaged();
public void FreeNative();
}
[CustomTypeMarshaller(typeof(CustomTypeMarshallerAttribute.GenericPlaceholder*[]),
CustomTypeMarshallerKind.LinearCollection, BufferSize = 0x200,
Features = CustomTypeMarshallerFeatures.UnmanagedResources | CustomTypeMarshallerFeatures.CallerAllocatedBuffer | CustomTypeMarshallerFeatures.TwoStageMarshalling)]
public unsafe ref struct PtrArrayMarshaller<T> where T : unmanaged
{
public PtrArrayMarshaller(int sizeOfNativeElement);
public PtrArrayMarshaller(T*[]? array, int sizeOfNativeElement);
public PtrArrayMarshaller(T*[]? array, Span<byte> buffer, int sizeOfNativeElement);
public ReadOnlySpan<IntPtr> GetManagedValuesSource();
public Span<IntPtr> GetManagedValuesDestination(int length);
public Span<byte> GetNativeValuesDestination();
public ReadOnlySpan<byte> GetNativeValuesSource(int length);
public ref byte GetPinnableReference();
public byte* ToNativeValue();
public void FromNativeValue(byte* value);
public T*[]? ToManaged();
public void FreeNative();
}
API Usage
The p/invoke source generator would use these marshallers for marshalling some types without requiring explicit user specification of the custom marshaller. The marshallers could also be explicitly used with the LibraryImportAttribute.StringMarshallingCustomType
property or the MarshalUsing
and NativeMarshalling
attributes.
[LibraryImport("NativeLib")]
internal static partial void Method([MarshalUsing(typeof(AnsiStringMarshaller))] string str);
[LibraryImport("NativeLib", StringMarshallingCustomType = typeof(AnsiStringMarshaller))]
internal static partial string Method(string s1, string s2);
Alternative Designs
The p/invoke source generator could inject marshallers / emit the code directly instead of using public marshaller types. However, that would be more of a size impact than having the types in runtime libraries. It would also mean that any issues in sensitive areas like string and array marshalling would require users to recompile to regenerate code, rather than being fixed by a newer runtime.
Risks
No response