51

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?

sharptooth
  • 167,383
  • 100
  • 513
  • 979
Nick
  • 2,913
  • 12
  • 40
  • 52
  • Possible duplicate of [How do I handle optional C++ dll struct arguments in C#](https://stackoverflow.com/questions/47997942/how-do-i-handle-optional-c-dll-struct-arguments-in-c-sharp) – River Dec 29 '17 at 19:43
  • 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. – Brain2000 Apr 08 '22 at 15:11

9 Answers9

49

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.

Andrew Hare
  • 344,730
  • 71
  • 640
  • 635
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.

sharptooth
  • 167,383
  • 100
  • 513
  • 979
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 4
    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? – Calmarius Apr 30 '09 at 05:26
  • @Calmarius Why not just write one definition replacing all `ref ` with `IntPtr`? Yes, it's less safe, but it seems if you deal with P/Invoke the code safety is already not the point. – SerG Jul 02 '14 at 12:57
11

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

Erich Mirabal
  • 9,860
  • 3
  • 34
  • 39
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.

Community
  • 1
  • 1
jonp
  • 13,512
  • 5
  • 45
  • 60
4
Mapping oMapping = null;

FILES_GetMemoryMapping(MapFile, out size, MapName, out PacketSize, ref oMapping, out PagePerSector);
Chris Doggett
  • 19,959
  • 4
  • 61
  • 86
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.

Jim Fell
  • 13,750
  • 36
  • 127
  • 202
  • 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)? – Athari Jun 07 '19 at 23:04
  • @Athari 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. – Jim Fell Jun 08 '19 at 02:53
2

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

ref Unsafe.AsRef<Mapping>(null)
Alpha Anar
  • 21
  • 1
  • 3
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);
}

Timur Vafin
  • 113
  • 1
  • 6
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/31807093) – Julia May 23 '22 at 10:57
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.

Jazimov
  • 12,626
  • 9
  • 52
  • 59