views:

173

answers:

2

Summary:

I have a bunch of C functions I have to call from C#. My current working solution is based on function overloading and I'm wondering if there is a more elegant solution.

The C stuff:

somewhere in a header file

typedef struct _unknown_stuff * handle; // a opaque pointer

an example of the function

func( uint      num_entries,
      handle *  objects,
      uint *    n)
{ ... }

in C the function should be used similar to this:

// warning: i bet that the syntax is not correct but you should get the idea...

uint n;

func(0, null, &n);

handle * objects = malloc(n * sizeof(handle));

func(n, objects, null);

The C# stuff:

right now I'm doing the following in C#:

public struct handle
{
    public IntPtr Pointer;
}

// version to get number of objects
[DllImport(dll, ...]
private static extern void
func(  uint        must_be_zero,
       object      must_be_null,
       out uint    n);

// version to get the actual data
[DllImport(dll, ...]
private static extern void
func(   uint           num_entries,
        [Out] handle[] objects,
        int            must_be_zero);

and then:

handle[] objects;

uint n = 42;

func(0, null, out n);

objects = new handle[n];

func(n, objects, 0);

The Question

Since I'm a C# noob, I was wondering if this is the best way to do this. Especially I would like to know if there is a way around overloading the function.

+1  A: 

First of all, your code as is is wrong, because in the second C# signature your third argument is int, while the corresponding C argument is still int*. It will work when targetting 32-bit Windows, because sizeof(int)==sizeof(int*) there - so when you pass a 0 int you end up passing a null pointer - but it is not 64-bit safe. If you still want to do it that way, you need to use IntPtr there.

Aside from that, you could try:

[DllImport(dll, ...]
private static extern void func(
  uint num_entries,
  [Out] handle[] objects,
  [Out] int[] num_devices);

making the third argument an array so that you could pass null there.

Or just use pointers. There's nothing bad about it, and don't be afraid of unsafe keyword - any kind of P/Invoke is inherently unsafe anyway, so you might as well be explicit about it.

Pavel Minaev
Ok, now I'm a little bit embarrassed... didn't know that C# supports pointers.Using pointers seem to work, thanks for the hint :)Off to the library and looking for a C# book
janjan
A: 

Wouldn't the C dll look at 0, and process that as null?

It only has one function, correct? That is, the C function is defined once, and not separately for a null case, and would have an

if( objects == NULL )
check somewhere in there. On most systems (check the stdio.h of the target system) this is 0. So calling from pinvoke with a 0 value in this place should do what you wanted overloading to do.

maxwellb
Yeah, you are right, but I had severe problems with type conversions so I decided to overload the methods.
janjan
If he uses the first C# signature (where 3rd argument is `out`), then passing 0 won't help - he'll have to declare a variable and assign 0 to that, and then pass it as `out`, and then the C function will actually get the address of that variable, with 0 in it.If he uses the second signature, then he can pass 0 (though I already explained above why it is a wrong thing to do), but he cannot use that to pass the actual int variable for the case where a value needs to be retrieved.
Pavel Minaev