It's fairly common, when working with PInvoke, to have a function that takes a MyStruct* as an argument. This isn't too difficult to work with; just define it on the managed side as ref MyStruct.
...right up until your native function also accepts NULL as valid input. Trying to call it this way won't work, because NULL is not assignment-compatible to a struct, and so you end up having to write an overload in your PInvoke header class that replaces it with an IntPtr, so that you can specifically pass IntPtr.Zero. And heaven help you if this function takes multiple struct pointers; you can end up with a combinatorial explosion of overloads very quickly!
A much better solution would be to create a System.Runtime.InteropServices.NullableRefAttribute that can be placed on an argument, which tells the compiler that it's valid to pass NULL here. It would only be valid to place NullableRefAttribute on a ref argument on a method that's tagged with DllImport (or whatever equivalent we end up using).
Obviously something like this would need to be implemented on the Roslyn side of things, but I'm raising it here first to ask people familiar with the CLR if it would require any changes to the runtime in order to support it? For example, is there any way that code that takes advantage of this might fail to verify, which would then require changes to the verifier?
Whilst I agree this is irritating, it is possible to just have the IntPtr variant, then marshal your struct manually to the IntPtr using Marshal.StructureToPtr . If you are going to wrap the method anyway as I think you probably would with a native API, this may be preferable.
Or use unsafe code and pointers directly.
*Hibernating Rhinos Ltd *
Oren Eini* l CEO l *Mobile: + 972-52-548-6969
Office: +972-4-622-7811 *l *Fax: +972-153-4-622-7811
On Mon, Jul 27, 2015 at 5:55 PM, Ben Pye [email protected] wrote:
Whilst I agree this is irritating, it is possible to just have the IntPtr
variant, then marshal your struct manually to the IntPtr using
Marshal.StructureToPtr . If you are going to wrap the method anyway as I
think you probably would with a native API, this may be preferable.—
Reply to this email directly or view it on GitHub
https://github.com/dotnet/coreclr/issues/1297#issuecomment-125236190.
I think a better alternative is to just marhsal Nullable<T>.
[DllImport("mylib")]
static extern void SomeFunction(ref MyStruct? parameter);
@ayende The problem then is that any code that even refers to one of your variables, even if you only care about them as opaque pointers, has to be marked unsafe, and it spreads like cancer throughout your codebase.
@OtherCrashOverride That might work as well. Would that have to be implemented in the marshaller, or in the compiler?
At this point, I really don't know. The implications of dotnet/runtime#4216 is that code generation is favored in the future so that makes it a compiler or external tool issue. As of today, it would be an issue for the runtime marshaller or MCG mentioned in the other thread.
You have always been able to use classes with layout as struct pointers.
Example:
[StructLayout(LayoutKind.Sequential)]
class Value
{
int moo;
}
extern static void Foo(Value v); // same as Foo(ref ValueTyped v)
Foo(null); // Value is basically a struct pointer.
And obviously this is useless for values ;p
For performance reasons, a lot of code uses structs rather than classes (stack versus heap). So changing a struct to class is not a realistic option for those cases.
I like @OtherCrashOverride suggestion of support marshalling nullable
@yizhang82 I opened an issue about marshalling generics a while back. IMO it may have made sense to not support them, from a difficulty-of-implementation perspective, back when generics first came out, but today it makes no sense at all and ought to be regarded as a bug to be fixed.
@masonwheeler Thanks for bringing this up. Let's discuss it in that issue.
I know this issue is ancient history at this point, but I stumbled upon it and didn't see this solution mentioned:
You can get a managed value type reference to null using ref Unsafe.AsRef<T>(null). It'll be marshaled as a null pointer as expected.
I don't believe these null references have any unintended side-effects. (Obviously you won't want to access fields on them though.) CoreLib already uses this in a handful of places, albeit not in an interop context.
Most helpful comment
I know this issue is ancient history at this point, but I stumbled upon it and didn't see this solution mentioned:
You can get a managed value type reference to null using
ref Unsafe.AsRef<T>(null). It'll be marshaled as a null pointer as expected.I don't believe these null references have any unintended side-effects. (Obviously you won't want to access fields on them though.) CoreLib already uses this in a handful of places, albeit not in an interop context.