I'm attempting to P Invoke a C library for use on a Xamarin android app.
Consider the following C structure:
typedef struct
{
bool myBool;
myOtherStruct sOtherStruct;
int myInt;
float myFloat;
float myFloatArray[1024];
float myFloatArray2[1024];
float myFloatArray3[20];
float myFloatArray4[30];
} sMyStruct;
This is called using the following function:
unsigned int initialise(sMyStruct* a_pMyStruct)
I've put this into a C# structure:
[StructLayout(LayoutKind.Sequential)]
public unsafe struct SMyStruct
{
bool myBool;
myOtherStruct sOtherStruct;
int myInt;
float myFloat;
public fixed float myFloatArray[1024];
public fixed float myFloatArray2[1024];
public fixed float myFloatArray3[20];
public fixed float myFloatArray4[30];
public unsafe float[] MyFloatArray
{
get
{
fixed (float* ptr = myFloatArray)
{
float[] array = new float[1024];
Marshal.Copy((IntPtr)ptr, array, 0, 1024 * sizeof(float));
return array;
}
}
}
public SMyStruct (bool MyBool, myOtherStruct otherStruct, int MyInt, float MyFloat)
{
myBool = MyBool;
sOtherStruct = myOtherStruct;
myInt = MyInt;
myFloat = MyFloat;
}
Here's my function in C# to invoke this:
[DllImport("libMylib")]
private static extern unsafe uint initialise(SMyStruct* a_pMyStruct);
I then call this function with:
public unsafe void init ()
{
SMyStruct initStruct;
uint result = initialise(&initStruct);
}
So what happens is the C function will return my structure with A LOT of data. I then pass the structure again into another C routine which uses these parameters for the rest of the program.
My issue is how do I get the float array data back into the correct structure variable so that I can pass it again? At current my code has been based on these questions: Marshalling float Array to c# and Marshalling complex struct to c#
But I've not managed to code this so I can pass the float array back to my struct without even seeing a compiler error (let alone failing when I test it!)
How do I get the float array data into my structure?
EDIT After a couple of answers and comments, I'm adding what I was doing initially to try and add some clarity.
I get a compiler error when rather than using the "public unsafe float[]..." routine above I do this (within the struct):
public SMyStruct (bool MyBool, myOtherStruct otherStruct, int MyInt, float MyFloat, float* MyFloatArray, float* MyFloatArray2, float* MyFloatArray3, float* MyFloatArray4)
{
myBool = MyBool;
sOtherStruct = myOtherStruct;
myInt = MyInt;
myFloat = MyFloat;
myFloatArray = MyFloatArray;
myFloatArray2 = MyFloatArray2;
myFloatArray3 = MyFloatArray3;
myFloatArray4 = MyFloatArray4;
}
With this code I get the error "You cannot use fixed size buffers contained in unfixed expressions. Try using the fixed statement" At this point I attempted to use the copy routine instead.
What I want is to ensure the fields myFloatArray, myFloatArray2 etc. to be populated with whatever the initialise function returns. FYI myBool, sOtherStruct etc. are populated as I expect.
If you don't need to access the data you can leave it as a pointer. Although it looks like you're responsible for the memory, so you'll need to alloc and later free the unmanaged memory you are using. Something like...
Note that you can still use the get the Marshaller to copy the pointer to your structure using
Marshal.PtrToStructure
but you no longer need to depend on it for your code.I suspect that many of your problems are caused by you attempting to run before you can walk. You have attempted to make a complex struct with many members. Make a single mistake in one place, and nothing works anywhere.
So, how can we simplify? Well, the question you ask relates to the fixed buffers. To paraphrase the question you are asking:
Let's deal with that by working with a simplified type that only contains a fixed buffer, and prove that we can copy to and from that buffer.
Your property getter is along the right lines. The biggest problem is that you pass an incorrect length. The final argument of the
Marshal.Copy
overload that you are calling is the number of elements. You erroneously pass the number of bytes. The setter is very similar in nature to the getter. It all looks like this:The output of the program is:
This building block shows you how to handle your fixed buffers. You can use the code from this program and be sure that the fixed buffers are handled correctly. When you move to a more complex structure, if you have any problems you can be confident that they are not related to the fixed buffer code.