Problems calling a C function containing pointers.

J

Johann Blake

I need to call a C DLL function. The first parameter expects a pointer
to a long. It returns a value at the address of the pointer. The second
parameter expects a pointer to a pointer. It creates an array and
returns the data it stores in this array.

I have tried many different combinations of P/Invoke declarations and
none have worked. When I step from my c# code into the c code, oNumRays
points to 0x00000000 and when it attempts to assign a value, an error
gets generated (something about object not set). Here are the
declarations I have tried:

public static extern Char SomeFunction(ref int numberOfRays, IntPtr
rayDirs);
public static extern Char SomeFunction(out int numberOfRays, IntPtr
rayDirs);
public static extern Char SomeFunction(IntPtr numberOfRays, IntPtr
rayDirs);
public static extern Char SomeFunction([MarshalAs(UnmanagedType.U4)]
out int numberOfRays, IntPtr rayDirs);
public static extern Char SomeFunction([MarshalAs(UnmanagedType.U4)]
int numberOfRays, IntPtr rayDirs);


Here is the C function...

static float sRayDirs[8];

GuiInterface_API char SomeFunction(long* oNumRays, float** oRayDirs)
{
*oNumRays = 12;

sRayDirs[0] = 1.1f;
sRayDirs[1] = 2.2f;
sRayDirs[2] = 3.3f;
sRayDirs[3] = 4.4f;
sRayDirs[4] = 5.5f;
sRayDirs[5] = 6.6f;
sRayDirs[6] = 7.7f;
sRayDirs[7] = 8.8f;

*oRayDirs = sRayDirs;

return 0;
}

Any suggestions on how to make this work?

Thanks for your help!
Johann Blake
 
N

Nicholas Paldino [.NET/C# MVP]

Johann,

The first declaration should work, but you have to correct the return
type:

public static extern byte SomeFunction(ref int number ofRays, IntPtr
rayDirs);

The reason for this is that a char in .NET is a 16-bit value, whereas in
C it is an 8-bit value.

You just have to make sure that you allocate the memory for the array
which the IntPtr points to before you make the call.

If you can use unsafe code, then you can simplify a lot of this by just
declaring the function with pointers, and then declare a pointer to the
array.

Hope this helps.
 
J

Johann Blake

Hi Nicholas,

I did correct the return type and used the first declaration I showed,
but it does not work and neither does using unsafe marked code.

Any other suggestions?

Thanks
Johann
 
N

Nicholas Paldino [.NET/C# MVP]

Johann,

You need to show how you are calling it, as the declarations are
correct.
 
W

Willy Denoyette [MVP]

Johann Blake said:
I need to call a C DLL function. The first parameter expects a pointer
to a long. It returns a value at the address of the pointer. The second
parameter expects a pointer to a pointer. It creates an array and
returns the data it stores in this array.

I have tried many different combinations of P/Invoke declarations and
none have worked. When I step from my c# code into the c code, oNumRays
points to 0x00000000 and when it attempts to assign a value, an error
gets generated (something about object not set). Here are the
declarations I have tried:

public static extern Char SomeFunction(ref int numberOfRays, IntPtr
rayDirs);
public static extern Char SomeFunction(out int numberOfRays, IntPtr
rayDirs);
public static extern Char SomeFunction(IntPtr numberOfRays, IntPtr
rayDirs);
public static extern Char SomeFunction([MarshalAs(UnmanagedType.U4)]
out int numberOfRays, IntPtr rayDirs);
public static extern Char SomeFunction([MarshalAs(UnmanagedType.U4)]
int numberOfRays, IntPtr rayDirs);


Here is the C function...

static float sRayDirs[8];

GuiInterface_API char SomeFunction(long* oNumRays, float** oRayDirs)
{
*oNumRays = 12;

sRayDirs[0] = 1.1f;
sRayDirs[1] = 2.2f;
sRayDirs[2] = 3.3f;
sRayDirs[3] = 4.4f;
sRayDirs[4] = 5.5f;
sRayDirs[5] = 6.6f;
sRayDirs[6] = 7.7f;
sRayDirs[7] = 8.8f;

*oRayDirs = sRayDirs;

return 0;
}

Any suggestions on how to make this work?

Thanks for your help!
Johann Blake

Your signature is not correct, 1) the return type should be a byte, 2) the
arrary pointer must be passed by ref.

Try this...

public static extern byte SomeFunction(ref int numberOfRays, ref IntPtr
rayDirs);

The float array must be marshaled to a managed float array, this has to be
done in code. V2.0 makes it a lot easier because it has a Marshal.Copy
overload that copies an unmanaged float array to a managed float array.

Here is how you can marshal an unmanaged float array to managed float array
....

byte b = SomeFunction(ref int numberOfRays, ref IntPtr rayDirs);

int unmPtr = rayDirs.ToInt32(); // non portable to 64 bit!
int sizeOfFloatArrayBytes = numberOfRays* 4 ; // 4 = sizeof(float)
byte[] bar = new byte[sizeOfFloatArrayBytes ];
for (int i = 0 ; i < sizeOfFloatArrayBytes ; i++ )
{
bar = Marshal.ReadByte(new IntPtr(unmPtr++));
}
float[] far = new float[numberOfRays];
for (int n = 0; n < numberOfRays; n++)
{
far[n] = BitConverter.ToSingle(bar, n * 4);
}
foreach(float f in far )
Console.WriteLine("{0}",f);

Willy.
 
J

Johann Blake

Nicholas,

I managed to get it to work. Don't ask me what I did that made it work.
The original C signature had a few more parameters preceding the ones
that I didn't post here. They were basic data types and not pointers. I
removed them and added them one at a time and verified that everything
was getting passed correctly. Eventually after adding each one, it all
worked, so I don't know what caused the problem.

As for the return data type being a bool, that turns out to be
incorrect. .NET returns a true if the C function returns 0. Willy's
post below is correct in that a byte is required. Not sure why .NET
returns a True when the function returns 0.

Thanks again,
Johann
 
J

Johann Blake

You're right. Surprisingly, a bool does not work as Nicholas suggested.
A bool returns true when the C function returns zero, which is wrong.

Johann
 
W

Willy Denoyette [MVP]

Johann Blake said:
You're right. Surprisingly, a bool does not work as Nicholas suggested.
A bool returns true when the C function returns zero, which is wrong.

Johann

I don't see any mention of a bool in your original posting, but if you are
talking about an API returning a bool, you need to know that C might have
different ways to express a bool, and you need to declare the function to
return a bool not a char or byte.

Note that the issue with your declaration was the missing ref for the second
argument, did it solve the problem?

public static extern byte SomeFunction(ref int numberOfRays, ref IntPtr
rayDirs);

or if the return is a 'bool' (C intrinsic type bool is one byte)

[return:MarshalAs(UnmanagedType.U1)]
public static extern bool SomeFunction(ref int numberOfRays, ref IntPtr
rayDirs);

or:

public static extern bool SomeFunction(ref int numberOfRays, ref IntPtr
rayDirs);
if the return type is a BOOL or BOOLEAN (typedefs in C)

Willy.
 
J

Johann Blake

The problem disappeared for some unknown reason. The ref was actually
the first thing I used. As for the return value, when the C function
returns a char and I use a bool, a returned value of 0 from C will
definitely result in a true in .NET. I have a feeling that C somehow
exports the return type as text and possibly converts a numerical 0 to
a character "0". Just my guess. But the correct solution is to use a
byte on the .NET side.

Johann
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top