将float [] []编组为float **

时间:2013-02-11 00:21:09

标签: c# c++ pinvoke marshalling

假设我有原型

的C ++函数
int someFunction(const float ** raws)

如何使用C#中的float[][]参数调用此函数?可能没有使用不安全的代码。

2 个答案:

答案 0 :(得分:4)

据我所知,你必须自己完成大部分工作。 Interop将帮助您编组顶级数组,但是您可以将所有嵌套数组固定,然后在完成后取消固定它们。此代码显示了一种方法:

using System;
using System.Runtime.InteropServices;

namespace ManagedClient
{
    class Program
    {
        [DllImport("UnmanagedDll.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern int UseFloats([MarshalAs(UnmanagedType.LPArray)] IntPtr[] raws);

        static void Main(string[] args)
        {
            float[][] data =
            {
                new[] { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f },
                new[] { 1.0f, 1.1f, 1.2f, 1.3f },
                new[] { 2.0f },
                new[] { 3.0f, 3.1f }
            };

            var handles = new GCHandle[data.Length];
            var pointers = new IntPtr[data.Length];

            try
            {
                for (int i = 0; i < data.Length; ++i)
                {
                    var h = GCHandle.Alloc(data[i], GCHandleType.Pinned);
                    handles[i] = h;
                    pointers[i] = h.AddrOfPinnedObject();
                }

                UseFloats(pointers);
            }
            finally
            {
                for (int i = 0; i < handles.Length; ++i)
                {
                    if (handles[i].IsAllocated)
                    {
                        handles[i].Free();
                    }
                }
            }
        }
    }
}

这会构建一个指针数组,每个指针都指向输入数组中的float数据。 (这是您的C函数期望数据到达的格式。)C代码如何知道每个子数组的长度取决于您。在我的测试代码中,我只是硬编码它以匹配传入的C#代码:

__declspec(dllexport) int __stdcall UseFloats(const float ** raws) 
{
    printf("%f %f %f %f %f\n", raws[0][0], raws[0][1], raws[0][2], raws[0][3], raws[0][4]);
    printf("%f %f %f %f\n", raws[1][0], raws[1][1], raws[1][2], raws[1][3], raws[0][4]);
    printf("%f\n", raws[2][0]);
    printf("%f %f\n", raws[3][0], raws[3][1]);
    return 0;
}

实际上,您可能想要做一些事情来告诉非托管代码每个子数组的长度 - 您选择的非托管函数签名不会以任何方式知道每个子数组的长度。 (据推测你使用float **的原因是因为你想要锯齿状数组。如果没有,如果每个子数组的长度完全相同,那么在这里使用矩形数组而不是数组的效率要高得多指针,它也会使编组更容易。)

答案 1 :(得分:2)

Ian已经回答了你的问题,我只想建议在C ++方面使用SAFEARRAY。

SAFEARRAY是C / C ++中模糊数组定义问题的COM答案,它们是包含底层元素的数量和大小的结构,并且更适合.Net数组。使用它们将允许从C#自动封送数组。

SAFEARRAY在C ++中使用很痛苦,但ATL有一些不错的包装器可以让事情变得更容易。如果可能的话,改变C ++函数来使用它,或者考虑编写一个C ++包装器来与使用它们的.Net进行互操作,并使用C ++编写的编组代码。