【问题标题】:Improper marshaling: C# array to a C++ unmanaged array封送处理不当:C# 数组到 C++ 非托管数组
【发布时间】:2011-11-10 17:03:02
【问题描述】:

我有以下 C# 代码,其中包含结构定义 (CInput)、obj 定义和 init,以及对 C++(本机)DLL 函数(也是我编写的)的调用。

//C# code

 public struct CInput
 {

  [MarshalAsAttribute(UnmanagedType.R8)] 
  public double Time;

  [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_R8)]
  public double[] Database;

  /* other similar fields*/

}     

CInput Inputs = new CInput();

/* init of Inputs fields*/

int bfr = Example(ref Inputs); //'Example' being the C++ DLL call

Messagebox.Show(bfr.ToString());

第二个参数的marshaling有错误,不知道在哪里。那么:

//C++ code

struct CInput {

  double Time;                       
  double Database[3650];     
  /*etc*/   
}

int Example(CInput& ObjIn) {

    return ObjIn.Database[0];        // just an example
}

如果我不小心并在数据库封送处理中仅指定“SafeArray”,我会收到“读/写内存错误,可能已损坏”等。

如果“数据库”被封送为 ByValArray 一切正常,则值会正确显示。不幸的是,我得到了一个内部大小异常,因为我有很多该大小的数组,因此我必须使用指针 - 但是任何带有“SizeArray”的东西都会带来以下结果(刚刚发布的代码):

(来自 C++):

Database[0] = **0**

Database[1..etc] = values of the next parameters in the struct marshaled with ByValArray.

我想我应该提一下,我需要从 C# 到 C++ 的相同结构,我不是在寻找任何花哨的东西。所以 Struct 中的数组 >>> Struct 中的数组。

任何对此的见解将非常有价值。我一直在寻找几个小时,但我还没有解决方案。

非常感谢。

【问题讨论】:

  • UnmanagedType.SafeArray 仅对 COM 代码有用。对应类型为SAFEARRAY,由COM Array functions管理

标签: c# c++ pinvoke marshalling


【解决方案1】:
公共结构 CInput { 公开双倍时间; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3650)] 公共双[]数据库; } CInput 输入 = 新 CInput(); int bfr = 示例(参考输入);

编辑。如果您需要动态分配数据库数组,则应更改 C++ 和 C# 代码。在 C++ 中,数据库应该定义为 double*,并且您需要在某处添加数组长度。在 C# 中,数据库应声明为 IntPtr,并使用 Marshal.AllocHGlobal 方法进行分配。请根据您的要求更正C++结构,然后C#代码可以据此修复。

【讨论】:

  • 非常感谢亚历克斯的回复。正如在我无法使用 ByVal 之后指定的那样,我必须使用指针......!有什么想法吗?
  • Inputs 参数通过引用传递,而不是通过值传递 - 帖子已被编辑。
  • @SlashVincent:你不能使用 C# 中的指针和 C++ 中的值——它们必须是两端的值。
  • @DeadMG 或两端的指针。可以是值或引用,只要双方匹配。
  • 我的意图可能表达错误,但我的意思是在两边都使用指针。
【解决方案2】:

据我了解您的问题,您不能将ByValArraySizeConst 一起使用,因为您的真实结构有大量这样的数组会导致堆栈溢出。

您认为也许您需要在结构中使用指针,我同意您的看法。这是怎么做的。

在 C++ 方面,您应该将每个数组声明为指向元素类型的指针:

struct CInput {
  double *array;
}

您可能还希望在结构中包含数组的长度以避免过多的硬编码常量。

所有艰苦的工作都发生在 C# 方面。

public struct CInput
{
    public IntPtr array;
}
...
double[] theArray = new double[3650];
CInput input = new CInput();
input.array = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(double))*theArray.Length);
try 
{
    Marshal.Copy(theArray, 0, input.array, theArray.Length);
    //call your C++ function here
}
finally
{
    Marshal.FreeHGlobal(input.array);
}

【讨论】:

  • 亲爱的大卫,您非常准确地一针见血!您提出的解决方案运行良好,非常感谢...!
  • 如果我想编组一组自定义对象而不是原语怎么办?
  • @Tsury 可能是调用Marshal.StructureToPtr 的数组循环。但我不想在不了解您的类型的情况下肯定地说。
  • @DavidHeffernan 感谢您的快速回答。我的类型在这里详述:stackoverflow.com/questions/29126836/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-15
  • 2015-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-09
  • 2015-10-26
相关资源
最近更新 更多