C#: How to pass null to a function expecting a ref?
Asked Answered
D

10

54

I've got the following function:

public static extern uint FILES_GetMemoryMapping(
    [MarshalAs(UnmanagedType.LPStr)] string pPathFile,
    out ushort Size,
    [MarshalAs(UnmanagedType.LPStr)] string MapName,
    out ushort PacketSize,
    ref Mapping oMapping,
    out byte PagesPerSector);

Which I would like to call like this:

FILES_GetMemoryMapping(MapFile, out size, MapName,
    out PacketSize, null, out PagePerSector);

Unfortunately, I cannot pass null in a field that requires type ref Mapping and no cast I've tried fixes this.

Any suggestions?

Doff answered 10/4, 2009 at 1:48 Comment(2)
Possible duplicate of How do I handle optional C++ dll struct arguments in C#Gustafson
Too bad C# won't work around optional refs like VB.net. Where it would create the dummy variable behind the scenes and just allow you to omit the parameter.Amphoteric
P
34

I'm assuming that Mapping is a structure? If so you can have two versions of the FILES_GetMemoryMapping() prototype with different signatures. For the second overload where you want to pass null, make the parameter an IntPtr and use IntPtr.Zero

public static extern uint FILES_GetMemoryMapping(
    [MarshalAs(UnmanagedType.LPStr)] string pPathFile,
    out ushort Size,
    [MarshalAs(UnmanagedType.LPStr)] string MapName,
    out ushort PacketSize,
    IntPtr oMapping,
    out byte PagesPerSector);

Call example:

FILES_GetMemoryMapping(MapFile, out size, MapName,
   out PacketSize, IntPtr.Zero, out PagePerSector);

If Mapping is actually a class instead of a structure, just set the value to null before passing it down.

Pumping answered 10/4, 2009 at 1:51 Comment(2)
And what do you suggest if you have a function with 8 pointer to structs and any of them can be null? Should I write 256 overloads? Using nullable value type will dummy variable works?Mutism
@Mutism Why not just write one definition replacing all ref <struct_name> with IntPtr? Yes, it's less safe, but it seems if you deal with P/Invoke the code safety is already not the point.Efrainefram
M
51

The reason you cannot pass null is because a ref parameter is given special treatment by the C# compiler. Any ref parameter must be a reference that can be passed to the function you are calling. Since you want to pass null the compiler is refusing to allow this since you are not providing a reference that the function is expecting to have.

Your only real option would be to create a local variable, set it to null, and pass that in. The compiler will not allow you to do much more than that.

Mclendon answered 10/4, 2009 at 1:52 Comment(3)
This is the only answer that gets to the heart of the matter.Evolve
Is there any way to do this with a DateTime? I need to pass in a DT value that does not exist.Bistro
@TomasBeblar, what about DateTime?, i.e. an optional type?Stephniestepladder
P
34

I'm assuming that Mapping is a structure? If so you can have two versions of the FILES_GetMemoryMapping() prototype with different signatures. For the second overload where you want to pass null, make the parameter an IntPtr and use IntPtr.Zero

public static extern uint FILES_GetMemoryMapping(
    [MarshalAs(UnmanagedType.LPStr)] string pPathFile,
    out ushort Size,
    [MarshalAs(UnmanagedType.LPStr)] string MapName,
    out ushort PacketSize,
    IntPtr oMapping,
    out byte PagesPerSector);

Call example:

FILES_GetMemoryMapping(MapFile, out size, MapName,
   out PacketSize, IntPtr.Zero, out PagePerSector);

If Mapping is actually a class instead of a structure, just set the value to null before passing it down.

Pumping answered 10/4, 2009 at 1:51 Comment(2)
And what do you suggest if you have a function with 8 pointer to structs and any of them can be null? Should I write 256 overloads? Using nullable value type will dummy variable works?Mutism
@Mutism Why not just write one definition replacing all ref <struct_name> with IntPtr? Yes, it's less safe, but it seems if you deal with P/Invoke the code safety is already not the point.Efrainefram
I
11

One way is to create a dummy variable, assign it null, and pass that in.

Imeldaimelida answered 10/4, 2009 at 1:50 Comment(0)
T
5

While @JaredPar's answer is undoubtedly the correct answer, there is another answer: unsafe code and pointers:

unsafe {
    Mapping* nullMapping = null;
    FILES_GetMemoryMapping(
            MapFile,
            out size,
            MapName,
            out PacketSize,
            ref *nullMapping,    // wat?
            out PagePerSector);
}

That looks like it should fail at runtime, but it doesn't, because the ref and the * cancel each other out, and the resulting value of ref *nullMapping is the null pointer, which is what FILES_GetMemoryMapping() will receive for that parameter.

This is probably not a good idea, but it's possible.

Tutt answered 22/10, 2015 at 14:43 Comment(0)
R
4
Mapping oMapping = null;

FILES_GetMemoryMapping(MapFile, out size, MapName, out PacketSize, ref oMapping, out PagePerSector);
Reentry answered 10/4, 2009 at 1:50 Comment(3)
Mapping is actually a struct so I cannot set it to null.Doff
@Nick, see my answer for the structure casePumping
I think this misses the point. Sometimes functions have ref parameters that you don't care about. Having to define useless variables clutters code and is annoying.Vocalism
P
3

Null can now be permitted using C# language 7.2 or greater. Simply replace the ref in your function parameter like so...

void MyFunc(ref Object obj) { ... }

to...

void MyFunc(in Object obj) { ... }

This will let you pass in null as a parameter value when calling the function in your application. It works the same for objects and native types and is syntactically equivalent to ref readonly.

Polston answered 7/6, 2019 at 18:1 Comment(2)
The problem with your approach is that while it allows passing null, it's no longer semantically correct, as the implementation of MyFunc can modify obj if a non-null value is passed, but your code says otherwise. Does .NET marshaller ignore the returned value (enforcing readonly restriction) or interprets in as ref (ignoring the restriction)?Lemus
@Lemus True enough. I just figured the usage for this out today. If your function also needs to modify the passed object, this solution may not be applicable, depending on the scope of the needed modification. For example, in some cases it may be acceptable to assign the passed read-only variable to a local variable to make modifications.Polston
A
2

You can get a null ref using System.Runtime.CompilerServices.Unsafe class.

ref Unsafe.AsRef<Mapping>(null)
Aa answered 3/7, 2022 at 12:24 Comment(0)
B
1

bro, then use pointers like in c

public static extern unsafe uint FILES_GetMemoryMapping(
    [MarshalAs(UnmanagedType.LPStr)] string pPathFile,
    out ushort Size,
    [MarshalAs(UnmanagedType.LPStr)] string MapName,
    out ushort PacketSize,
    Mapping* oMapping,
    out byte PagesPerSector);

// somewhere in code
unsafe {
    uint result = FILES_GetMemoryMapping("path", out ushort size, "map", out ushort packetSize, null, out byte pages);
}

Beauvoir answered 18/5, 2022 at 17:24 Comment(1)
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From ReviewPhototransistor
M
0

Perhaps its not exactly the ideal answer, but if you need to pass null as a parameter when calling a function, consider making an overload of that function that omits the formal parameter for the variable you're trying to set to null.

For example, let's say you have a function that looks like this:

public void MyFunction(string x, int y, ref string z) {...};

You want to be able to pass null for parameter z. Try instead creating a new MyFunction overload that looks something like this:

public void MyFunction(string x, int y) {...};

This approach won't suit everyone's needs, but it's another possible solution.

Mildew answered 22/6, 2018 at 4:30 Comment(0)
O
0

System.Runtime.CompilerServices.Unsafe:

ref Unsafe.NullRef<Mapping>()

This doesn't require enabling unsafe code in the project.

Orchid answered 13/6 at 19:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.