【问题标题】:PInvoke Pointer to struct including float arrayPInvoke 指向结构的指针,包括浮点数组
【发布时间】:2015-08-16 00:48:01
【问题描述】:

我正在尝试 P 调用 C 库以在 Xamarin android 应用程序上使用。

考虑以下 C 结构:

typedef struct
{
    bool myBool;
    myOtherStruct sOtherStruct;
    int myInt;
    float myFloat;
    float myFloatArray[1024];
    float myFloatArray2[1024];
    float myFloatArray3[20];
    float myFloatArray4[30];
} sMyStruct;

这是使用以下函数调用的:

unsigned int initialise(sMyStruct* a_pMyStruct)

我已将其放入 C# 结构中:

[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;
}

这是我在 C# 中调用它的函数:

[DllImport("libMylib")]
private static extern unsafe uint initialise(SMyStruct* a_pMyStruct);

然后我调用这个函数:

public unsafe void init ()
{
    SMyStruct initStruct;
    uint result = initialise(&initStruct);
}

因此,C 函数将返回包含大量数据的结构。然后我再次将该结构传递给另一个 C 例程,该例程将这些参数用于程序的其余部分。

我的问题是如何将浮点数组数据返回到正确的结构变量中,以便再次传递它?目前我的代码基于这些问题: Marshalling float Array to c#Marshalling complex struct to c#

但我没有设法编写此代码,因此我可以将浮点数组传回我的结构,甚至不会看到编译器错误(更不用说在测试时失败了!)

如何将浮点数组数据放入我的结构中?

编辑 经过几个答案和 cmets,我正在添加我最初正在做的事情以尝试增加一些清晰度。

当我没有使用上面的“public unsafe float[]...”例程时,我得到了一个编译器错误(在结构内):

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;
}

使用此代码,我收到错误“您不能使用包含在未固定表达式中的固定大小缓冲区。尝试使用固定语句” 此时我尝试改用复制例程。

我想要的是确保字段 myFloatArray、myFloatArray2 等被初始化函数返回的任何内容填充。仅供参考,myBool、sOtherStruct 等按我的预期填充。

【问题讨论】:

  • 您真的需要访问数据吗?还是您只是将其传递给另一个外部(非托管)方法?如果您不需要从 .Net 访问它,您可以只分配一个 IntPtr 并将其提供给下一个方法
  • @JamesBarrass 我实际上没有,我一直想输出数据,以便我可以证明它作为中间步骤工作。您可以指出 Alloc 和 IntPtr 的一个很好的例子吗?谢谢。
  • 很难猜测“回传”可能意味着什么,这个问题缺少实际生成错误的代码。
  • @HansPassant 我已经编辑了我的问题以显示我之前在做什么会激活错误。

标签: c# arrays pointers struct pinvoke


【解决方案1】:

如果您不需要访问数据,可以将其保留为指针。虽然看起来您对内存负责,但您需要分配并稍后释放您正在使用的非托管内存。有点像...

[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);

请注意,您仍然可以使用获取 Marshaller 来使用 Marshal.PtrToStructure 将指针复制到您的结构,但您的代码不再需要依赖它。

【讨论】:

  • 这看起来很有希望,但我的代码超出了我的预期。正如您所提到的,使用 PtrToStructure 仍然不应该填充浮点数组。
  • myOtherStruct 有多大?您将能够声明一个结构大小的字节数组,并将 Marshal.Copy 声明到其中以检查结构的内容。如果都是零,那么初始化是否正常工作?它的返回码是否表示错误?
  • myOtherStruct 包含 2 个无符号整数和 2 个浮点数,我的示例与我的实际结构略有不同,它包含 1 个 bool、1 个 myOtherStruct、2 个浮点数、1 个 uint、4 个 1024 浮点数组、1 个浮点数30 的数组和 1 个浮点数组 20。结果是 16613,此函数的返回码不会返回错误。但是后面的函数返回一个错误,表明在这个函数中发送了不正确的数据。
  • 我会尝试创建一个新字节 [16613] 并使用 marshal.Copy 将数据从指针复制到数组。查看数组,从第 30 个字节开始的任何内容都是浮点数组的一部分。数据是空的还是已经填充了字节?您可以使用BitConverter.ToSingle(byteArray, 29) 查看第一个浮点数在字节数组中的实际值。
  • 我不明白你为什么要让生活如此艰难。
【解决方案2】:

我怀疑您的许多问题是由于您在走路之前尝试跑步造成的。您试图创建一个包含许多成员的复杂结构。在一个地方犯一个错误,在任何地方都没有任何效果。

那么,我们怎样才能简化呢?好吧,您提出的问题与固定缓冲区有关。套用你所问的问题:

如何将数组复制到固定缓冲区或从固定缓冲区复制?

让我们处理一个只包含固定缓冲区的简化类型,并证明我们可以复制到该缓冲区并从该缓冲区复制。

您的属性吸气剂是正确的。最大的问题是您传递的长度不正确。您调用的Marshal.Copy 重载的最后一个参数是元素的数量。您错误地传递了字节数。 setter 在本质上与 getter 非常相似。一切看起来像这样:

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();
        }
    }
}

程序的输出是:

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

此构建块向您展示如何处理固定缓冲区。您可以使用该程序中的代码并确保正确处理固定缓冲区。当您移动到更复杂的结构时,如果您有任何问题,您可以确信它们与固定缓冲区代码无关。

【讨论】:

  • 谢谢。我已经编译了它并且它有效。我现在在我自己的程序中遇到的问题是,每当我想查看“_floatarray”的值时,都会显示“{ConsoleApplication1.MyStruct.<_floatarray>e_FixedBuffer0”,展开它然后只显示“FixedElementField | 1”至少我希望这是一个 4 的数组,可能是 0。(我补充说,它在你的程序中)。显然,我缺少一些东西,这就是它没有正确显示的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-03
  • 1970-01-01
  • 2012-12-07
  • 2016-06-20
相关资源
最近更新 更多