tags:

views:

556

answers:

5

Consider the following DllImport:

[DllImport("lulz.so")]
public static extern int DoSomething(IntPtr SomeParam);

This is actually referencing a C style function like this:

int DoSomething(void* SomeParam);

Consider that SomeParam is an "out" param, but can also be NULL. The C function behaves differently if the param is NULL. So I would probably want:

[DllImport("lulz.so")]
public static extern int DoSomething(out IntPtr SomeParam);

But, if I make it an out param in my import, I cannot pass it NULL, i.e. I can't do this:

int retVal = DoSomething(IntPtr.Zero)

What are my options here?

+7  A: 

If you're trying to pass a value, then out is not the right keyword; change it to ref. You'll still need to explicitly pass a variable, but it can be a null reference.

For example...

[DllImport("lulz.so")]
public static extern int DoSomething(ref IntPtr SomeParam);

You can then call it like this:

IntPtr retVal = IntPtr.Zero;

DoSomething(ref retVal);

However

What is telling you that it needs to be either out or ref? Passing an IntPtr as out or ref is really akin to passing a double pointer. It would actually seem more appropriate to pass the parameter as an IntPtr.

The typical procedure is either to allocate the necessary memory in managed code and pass an IntPtr representing that allocated memory, or IntPtr.Zero to represent a null pointer. You do not need to pass the IntPtr as out or ref in order to send data back to .NET; you only need to do that if the function you're calling would actually change the pointer's address.

Adam Robinson
Can you give an example?
Stephen Cox
Perhaps I am missing something, but the C lib sees the passed in variable as a initialized memory address when passed with ref, and not NULL. The only way I can get it to see NULL, is by passing it with no modifier, like this: [DllImport("lulz.so")]public static extern int DoSomething(IntPtr SomeParam);and doing: DoSomething(IntPtr.Zero);What gives?
Stephen Cox
Adam, the function I'm actually calling (this is just an example) will ultimately marshal data back up to C#.
Stephen Cox
A: 

I don't understand what the problem is....

This runs:

private void button2_Click(object sender, EventArgs e) {
    object bar;
    Method(out bar);

    bar = IntPtr.Zero;
    Method(out bar);
}

private void Method(out object foo) {
    foo = null;
}
recursive
Would you be truly passing null there in an interop setting, though? Wouldn't you be passing the address of bar?
Stephen Cox
An out parameter is never really passed in. I don't know how it'd be handled with interop, but in the case of a normal out variable the compile enforces the function setting the variable before it can be used, and it must also be set before the function ends, which means that the input is irrelevant.
Matthew Scharley
I know that the value of an out parameter that's passed is irrelevant. But that's what was asked for...
recursive
@recursive: it was a comment to answer Stephen's question. Even if out does work, it's a semantic error of quite a large magnitude to use it in this case because an `out` parameter isn't passed in to a function in the normal sense of that.
Matthew Scharley
A: 

What's the intention of passing NULL? Is it intended to call the method as usual, but to simply not set the output parameter?

In that case, I think I'd just wrap the extern method with an overload in C#. That overload (without the out parameter) would be like this:

public void DoSomething()
{
    IntPtr dummy;
    DoSomething(out dummy);
}
John Saunders
Correct. The function just does something different if the input variable is NULL as opposed to holding some allocated memory to populate.
Stephen Cox
If it really does something different, then don't use my example. My example assumed the only difference was whether the out parameter was populated, hence I felt an overload was ok.
John Saunders
I guess I could also provide two DllImports of the same function, one with the param marked as out, and one not? The one not marked with out would accept NULL? Seems sketchy to do that though.
Stephen Cox
`ref` and `out` are the same thing, they only provide different semantics inside the function itself: namely that an `out` parameter must be set inside the function before it is used, and must be set before the function returns. The underlying code for both are the same though as far as I'm aware.
Matthew Scharley
@Matthew: For standard .NET calls you're correct as far as I know. The difference in keyword DOES make a difference when you're talking about interop marshaling, however.
Adam Robinson
A: 

I ran into this once. I ended up marshaling the pointer myself (see Marshal Members for the library functions to do so).

Joshua
A: 

Personally, I'd import this function twice, first time with 'out' parameter, second with 'in'.

[DllImport("lulz.so")]
public static extern int DoSomething(out IntPtr SomeParam);

// call as DoSomethingWithNull(IntPtr.Zero)
[DllImport("lulz.so", EntryPoint="DoSomething")]
public static extern int DoSomethingWithNull(IntPtr SomeParam);

This will solve your problem and will make code more readable.

elder_george