dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.98k stars 4.66k forks source link

How to marshall null terminated array on .NET 7 using LibraryImport and ContiguousCollectionMarshaller #89389

Open mveril opened 1 year ago

mveril commented 1 year ago

Hello How to implement a marshalller for a null terminated contiguous collection on .NET 7 using LibraryImport and ContiguousCollectionMarshallerAttribute ? I currently work on a wrapper around libimobiledevice called MobileDeviceSharp the aim of this project is to implement a fully object-oriented wrapper around this C library in order to work with it as a standard .NET way. For many collections returned by the C library we don't have any out parameter to know the size of the returned collection. The count is intended to be determined at runtime using the null terminated beaviour of the returned collection exactly like the string marshaller do for strings. So, I cannot use CountElementName. For DllImport I found a workaround using generic ICustomMarshaller that take another ICustomMarshaller as type parameter in order to use it for marshalling elements. So what is the good way to work with Null terminated array? If this behaviors is not currently handled by .NET ContiguousCollectionMarshaller it should be interesting to handle this case. Here an example of method with a null terminated array of string I need to marshal, https://github.com/libimobiledevice/libimobiledevice/blob/6fc41f57fc607df9b07446ca45bdf754225c9bd9/include/libimobiledevice/afc.h#L147-L158 Here on this afc_read_directory method the directory_information parameter is a null terminated array of strings with no extra information for size. So, to treat it we need to loop until null pointer. Thank you in advance for your answers.

ghost commented 1 year ago

Tagging subscribers to this area: @dotnet/interop-contrib See info in area-owners.md if you want to be subscribed.

Issue Details
How to implement a marshalller for a null terminated contiguous collection on .NET 7 using [LibraryImport](https://learn.microsoft.com/dotnet/standard/native-interop/pinvoke-source-generation) and [ContiguousCollectionMarshallerAttribute](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshalling.contiguouscollectionmarshallerattribute?view=net-7.0) ? I currently work on a wrapper around [libimobiledevice](https://libimobiledevice.org called [MobileDeviceSharp](https://github.com/mveril/MobileDeviceSharp) the aim of this project is to implement a fully object-oriented wrapper around this C library in order to work with it as a standard .NET way. For many collections returned by the C library we don't have any out parameter to know the size of the returned collection. The count is intended to be determined at runtime using the null terminated beaviour of the returned collection exactly like the string marshaller do for strings. So, I cannot use [CountElementName](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.marshalling.marshalusingattribute.countelementname?view=net-7.0] For DllImport I found a workaround using generic [ICustomMarshaller](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.icustommarshaler?view=net-6.0) that take another [ICustomMarshaller](https://learn.microsoft.com/dotnet/api/system.runtime.interopservices.icustommarshaler?view=net-6.0) as type parameter in order to use it for marshalling elements. So what is the good way to work with Null terminated array? If this behaviors is not currently handled by .NET ContiguousCollectionMarshaller it should be interesting to handle this case. Here an example of method with a null terminated array of string I need to marshal, https://github.com/libimobiledevice/libimobiledevice/blob/6fc41f57fc607df9b07446ca45bdf754225c9bd9/include/libimobiledevice/afc.h#L147-L158 Here on this `afc_read_directory` method the `directory_information` parameter is a null terminated array of strings with no extra information for size. So, to treat it we need to loop until null pointer. Thank you by advance for your answers.
Author: mveril
Assignees: -
Labels: `area-System.Runtime.InteropServices`
Milestone: -
jkoritzinsky commented 1 year ago

@mveril, the ContiguousCollectionMarshaller shape doesn't support this shape of a collection (null terminated instead of a specified length). I'd recommend writing a non-collection marshaller that handles collections of your particular element and manually marshalling the elements. I know it isn't as smooth of a solution, but it's the best solution I can offer today.

We'll investigate implementing a marshaller model for this in the future, but I'm not sure when we'll get to it.

mveril commented 1 year ago

Hello @jkoritzinsky thanks for your reponse. Ok I will do ad hoc CustomMarshaller for it. I see two possible solutions for this issue for a future version of .NET.

  1. By analogy with
 public static TNative AllocateContainerForUnmanagedElements(TCollection managed, Span<TOther> buffer, out int numElements);

and its statefull equivalent we can think about a

public static TCollection AllocateContainerForManagedElements(TNative unmanaged, out int numElements);

That give the number of elements to the pinvoke code. to allow to marshal ContiguousCollection with specific counting rules.

  1. A specific NullTerminatedCountiguousCollectionMarshallerAttribute attribute with the counting fully handled by the pinvoke source generator.

In each of these solutionsConstantElementCount or CountElementName should not be set.