0

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.

4
  • Do you actually need to access the data? or are you just passing it to another external (non managed) method? You could just Alloc an IntPtr and feed that onto the next method if you don't need to access it from .Net Commented Jun 2, 2015 at 9:44
  • @JamesBarrass I actually don't, I've been wanting to output the data so I can prove it's working as an in between step. Is there a good example for Alloc and IntPtr you can point me to? Thanks. Commented Jun 2, 2015 at 9:56
  • Hard to guess what "passing back" might mean, this question is missing the code that actually generates the error. Commented Jun 2, 2015 at 10:37
  • @HansPassant I've edited my question to show what I was doing previously which activates the error. Commented Jun 2, 2015 at 11:51

2 Answers 2

0

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...

[DllImport("libMylib")]
private static extern uint initialise(IntPtr a_pMyStruct);

[DllImport("libMylib")]
private static extern uint anotherNativeMethod(IntPtr a_pMyStruct);

//...

//How big is myOtherStruct??
int size = 1 + ?? + 4 + 4 + (1024*4) + (1024*4) + (20*4) + (30*4);
//int size = Marhsal.SizeOf(SMyStruct);
IntPtr pMyStruct = Marshal.AllocHGlobal(size);
initialise(pMyStruct);
anotherNativeMethod(pMyStruct);
Marshal.FreeHGlobal(pMyStruct);

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.

Sign up to request clarification or add additional context in comments.

7 Comments

This looks hopeful, but my code beyond that still doesn't work as I would expect. Using the PtrToStructure as you mention, still doesn't should the float arrays populated.
How big is myOtherStruct? You'll be able to declare a byte array the size of the structure and Marshal.Copy into it to examine the contents of the structure. If it's all zero's then is initialise working correctly? does it's return code indicate an error?
The myOtherStruct contains 2 unsigned ints, and 2 floats, my example is slightly different to my actual struct which contains 1 bool, 1 myOtherStruct, 2 float, 1 uint, 4 float arrays of 1024, 1 float array of 30, and 1 float array 20. Which works out to be 16613, the return code on this function does not return an error. But a later function, returns an error which indicates that incorrect data is being sent in this function.
I'd try creating a new byte[16613] and using marshal.Copy to copy the data from the pointer to the array. Have a look through the array and anything from the 30th byte onwards is part of your float arrays. Is the data empty or have the bytes been populated? You can use BitConverter.ToSingle(byteArray, 29) to look at the actual value of the first float once it's in the byte array.
I don't understand why you would want to make life so hard.
|
0

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:

How can I copy an array to and from a fixed buffer?

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:

using System;
using System.Text;
using System.Runtime.InteropServices;

namespace ConsoleApplication1
{
    public unsafe struct MyStruct
    {
        private const int floatArrayLength = 4;

        private fixed float _floatArray[floatArrayLength];

        public float[] floatArray
        {
            get
            {
                float[] result = new float[floatArrayLength];
                fixed (float* ptr = _floatArray)
                {
                    Marshal.Copy((IntPtr)ptr, result, 0, floatArrayLength);
                }
                return result;
            }
            set
            {
                int length = Math.Min(floatArrayLength, value.Length);
                fixed (float* ptr = _floatArray)
                {
                    Marshal.Copy(value, 0, (IntPtr)ptr, length);
                    for (int i = length; i < floatArrayLength; i++)
                        ptr[i] = 0;
                }
            }
        }
    }

    class Program
    {
        static void WriteArray(float[] arr)
        {
            foreach (float value in arr)
            {
                Console.Write(value);
                Console.Write(" ");
            }
            Console.WriteLine();
        }

        static void Main(string[] args)
        {
            MyStruct myStruct = new MyStruct();
            WriteArray(myStruct.floatArray);
            myStruct.floatArray = new float[] { 1, 2, 3, 4 };
            WriteArray(myStruct.floatArray);
            myStruct.floatArray = new float[] { 5, 6 };
            WriteArray(myStruct.floatArray);
            Console.ReadLine();
        }
    }
}

The output of the program is:

0 0 0 0
1 2 3 4
5 6 0 0

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.

1 Comment

Thank you. I've compiled this and it works. The issue I have on my own program now is that whenever I want to look at the value for "_floatarray" I am shown "{ConsoleApplication1.MyStruct.<_floatarray>e_FixedBuffer0", expanding this then just shows me "FixedElementField | 1" At the very least I'm expecting this to be an array of 4, probably full of 0. (Which I add, it does on your program). Clearly there's something I'm missing which is why it's not showing up correctly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.