【问题标题】:DllImport, C/C++ -> C#, out array of doublesDllImport,C/C++ -> C#,出双打数组
【发布时间】:2021-06-25 05:13:25
【问题描述】:

我想从 C++ 中获取双精度数组。
我用整数数组进行了测试,它工作正常,但我不知道双精度数组不能工作。
双精度数组的第一个和第二个元素是错误的。

还有一个奇怪的地方,我把FreeCoTaskMem注释掉了,因为不注释的话应用会崩溃,int和double是一样的。

任何提示,谢谢。

C/C++ 代码

#define DllExport __declspec(dllexport)
extern "C"
{       
   DllExport void TestOutOfInts(int** ints);
   DllExport void TestOutOfDoubles(double** doubles);
}

DllExport void TestOutOfInts(int** ints)
{
    int xyzs[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    (*ints) = xyzs;
}

DllExport void TestOutOfDoubles(double** doubles)
{   
    double xyzs[9] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9 };   
    (*doubles) = xyzs;
}

C#代码

[DllImport("MeshToolsWrapper.dll")]
static extern void TestOutOfInts(out IntPtr ints);

[DllImport("MeshToolsWrapper.dll")]
static extern void TestOutOfDoubles(out IntPtr doubles);

TestOutOfInts(out IntPtr ints);
int[] test1 = new int[9];
Marshal.Copy(ints, test1, 0, 9);
//Marshal.FreeCoTaskMem(ints);

TestOutOfDoubles(out IntPtr doubles); 
double[] test2 = new double[9];
Marshal.Copy(doubles, test2, 0, 9);
//Marshal.FreeCoTaskMem(doubles);

C# 结果

# ints
1
2
3
4
5
6
7
8
9

# doubles
1.16501519399776E-311
6.95245618745976E-310
3.3
4.4
5.5
6.6
7.7
8.8
9.9

【问题讨论】:

  • 两个数组都是本地的。整数一是偶然的。
  • 让 C# 传递数组以供 C++ 填充。这样 C# 拥有内存(编辑:我的意思是 C# 负责保留/分配和释放/解除分配,我请您记住哪个运行时有垃圾收集器),而 C++ 只是借用。
  • 啊,原来如此,难怪,非常感谢大家。
  • 嗯……您可能在这里得出错误的结论。垃圾收集器没有错。正如公认的答案所提到的,数组在堆栈中分配,因此在函数返回时它被释放。不涉及垃圾收集器。您可以让 C++ 拥有内存,例如通过使用 new 分配,但是您需要记住告诉 C++ 去分配。但是,.NET 有一个垃圾收集器,这使得 C# 可以方便地拥有内存。此外,当调用者拥有内存时,通常更容易跟踪内存是否仍然有效。
  • 您可能还想将调用约定设置为Cdecl

标签: c# c++ arrays dllimport


【解决方案1】:

这个:

DllExport void TestOutOfDoubles(double** doubles)
{   
    double xyzs[9] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9 };   
    (*doubles) = xyzs;
}

即使没有 C# 互操作,也是未定义的行为。您正在返回一个指向堆栈内存的指针(数组)。下一个函数调用和xyzs 的内容被破坏。几乎可以保证。

TestOutOfInts 函数也是如此。你很幸运,它似乎有效。

这是解决方法:

DllExport void TestOutOfDoubles(double** doubles)
{   
    double xyzs[9] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9 };   
    double* result = new double[9];
    std::copy(xyzs, xysz+9, result);
    
    (*doubles) = result;
}

但是您需要提供另一个函数来释放分配的内存以供 C# 代码调用。

更好的方法是强制调用者进行分配。有不同的技术,但我建议:

DllExport void TestOutOfDoubles(double* doubles, unsigned int size)
{   
    double xyzs[9] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9 };   

    if (size > 9)
    {
        size = 9;
    }

    std::copy(doubles, xyzs, xysz+size);

}

还有一个提前检索尺寸的函数。

DllExport unsigned int GetDoubleArrayLength()
{   
   return 9; // or whatever the actual size is    
}

然后由 C# 代码进行数组分配。

【讨论】:

  • 酷,谢谢你的分配建议。
猜你喜欢
  • 2019-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-11
  • 2011-05-24
  • 1970-01-01
相关资源
最近更新 更多