PInvoke Pointer to struct including float array

2019-09-20 14:41发布

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.

2条回答
2楼-- · 2019-09-20 15:29

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.

查看更多
Root(大扎)
3楼-- · 2019-09-20 15:40

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.

查看更多
登录 后发表回答