2013-02-11 37 views
1

比方说,我有原型编组浮法[] []为float **

int someFunction(const float ** raws) 

我如何可以调用这个函数与C#一个float[][]参数C++函数?可能没有使用不安全的代码。

+4

'float [] []'与'float **'根本不同,所以可能不会。 – 2013-02-11 00:21:52

+2

好吧,它不应该是不可能的。我确信有一种方法。这就是我称之为“编组”的原因。 – hattenn 2013-02-11 00:22:44

+0

我的意思是这样做的方法是将float [] []放入类/结构体中,然后将其传递。 – 2013-02-11 00:25:03

回答

3

就我所知,你必须自己完成大部分工作。 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 **的原因是因为你想要锯齿状的数组,如果没有,并且如果每个子数组的长度完全相同,那么在这里使用矩形数组会更有效率,而不是指针,这将使编组更容易太)。

+0

很好的答案。你可能的意思是“可能你使用float [] []”的原因不是“float **”,应该用下划线或粗体表示,因为如果是这种情况,可以自动编组二维数组(float [,])。 – 2013-02-11 10:56:41

+0

谢谢!但我的意思是我写了:我责怪“float **”,因为这是他试图调用的非托管函数的签名。这是造成问题的根本原因。 (如果非托管签名继续为float,切换到C#中的float [,]并不会有多大帮助** - 非托管签名需要一个指针数组,这是问题的核心,以float [,]开头意味着你只需要固定一个对象,但你仍然需要构建一个IntPtr []。)我认为最好的解决方案是如果可能的话更改非托管签名。 – 2013-02-11 13:34:43

+0

@Ian为什么你使用'[MarshalAs(UnmanagedType.LPArray)]'?另外,如果您不使用该属性,那么默认的编组是什么? – hattenn 2013-02-11 17:36:04

1

伊恩已经回答了你的问题,我只想建议在C++端使用SAFEARRAY。

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

SAFEARRAY在C++中使用起来很痛苦,但ATL有一些很好的包装让事情变得更简单。如果在所有可能的情况下改变C++函数来使用它,或者考虑编写一个与使用它们的.Net互操作的C++包装器,并且使用C++编写编组代码。

+0

不幸的是,C++代码是由其他人编写的,我没有太多的控制权。虽然欣赏这个建议! – hattenn 2013-02-11 17:42:56