【问题标题】:Trouble marshaling array of struct data from C++ to C#将结构数据数组从 C++ 编组到 C# 时遇到问题
【发布时间】:2012-10-24 16:22:56
【问题描述】:

我已经搜索了几天并尝试了我能找到的所有东西,但仍然无法让它工作。

详情: 我有一个第 3 方股票交易应用程序正在调用我的非托管 dll。它提供 dll 处理/过滤然后保存到全局环形缓冲区的数据。环形缓冲区是一个结构数组,长度为 100。所有这些都在股票交易应用程序进程中运行。

我还有一个托管 C# 应用程序在不同的进程中调用相同的 dll,它需要尽可能快速有效地获取全局环形缓冲区中的信息。一切正常,除了我只能获取数组中第一个结构的数据。同样在从 C# 调用 dll 之后,C# 代码不再知道 arrayMD 是一个结构数组,它在调试器中显示为一个简单的结构。难道是dll中的memcpy导致了问题?我用 [In, Out]、IntPtr 和 Marchal.PtrToStructure 组合尝试了各种组合。我非常fubar。任何帮助将不胜感激。

谢谢

这是我正在尝试的。 在dll方面:

struct stMD
{
  float Price;
  unsigned int  PriceDir;
  unsigned int  PriceDirCnt;
};

// Global memory
#pragma data_seg (".IPC")
    bool NewPoint = false;      // Flag used to signal a new point.
    static stMD aryMD [100] = {{0}};
#pragma data_seg()

void __stdcall RetrieveMD (stMD *LatestMD [])
{
    memcpy(*LatestMD, aryMD, sizeof(aryMD));
}

在 C# 方面:

[StructLayout(LayoutKind.Sequential)]
public struct stMD
{
    public float Price;
    public uint PriceDir;
    public uint PriceDirCnt;
};

public static stMD[] arrayMD = new stMD[100];

[DllImport(@"Market.dll")]
public static extern void RetrieveMD(ref stMD[] arrayMD);

RetrieveMD(ref arrayMD);

【问题讨论】:

标签: c# c++ arrays structure marshalling


【解决方案1】:

问题在于你的 DLL 入口点的定义:

void __stdcall RetrieveMD (stMDP *LatestMD []) 

您没有指定数组的大小,那么 C# 应该如何知道复制了多少元素?这在其他语言中也是一个问题。您的实现只是假设提供的内存足够大以包含 aryMD。但如果不是呢?您刚刚创建了缓冲区溢出。

如果要让调用者分配数组,那么调用者还必须传入数组包含的元素个数。

编辑

C++ 声明应如下所示:

// On input, length should be the number of elements in the LatestMD array.
// On output, length should be the number of valid records copied into the array.
void __stdcall RetrieveMD( stMDP * LatestMD, int * length );

C# 声明将如下所示:

[DllImport(@"Market.dll")]
public static extern void RetrieveMD(
    [In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] ref stMD[] arrayMD,
    [In, Out] ref int length);

【讨论】:

    【解决方案2】:

    我认为,您的问题是您试图通过引用将数组从 C# 传递到 C,而您几乎不需要这样做。在您的情况下,您想要发生的只是让 C# 为 100 个结构分配内存,然后将该内存传递给 C 进行填充。在您的情况下,您将传递一个 指向 的指针一个结构数组,这是您并不真正需要的额外间接级别。

    您很少看到采用固定大小数组的 C 函数;相反,您的函数通常会在 C 中定义以接受指针和长度,例如类似:

    void __stdcall RetrieveMD (stMDP *LatestMD, int size) 
    {
       int count = 0;
       if (size < sizeof(aryMD))
         count = size;
       else
         count = sizeof(aryMD);
    
       memcpy(LatestMD, aryMD, count);      
    }
    

    要从 C# 调用此方法,您需要做两件事。首先,您需要分配一个适当大小的数组以传入。其次,您需要告诉编组代码(执行 C# -> C 复制)如何计算要传递多少数据通过[MarshalAs] 属性进行编组:

    [DllImport(@"Market.dll")]
    public static extern void RetrieveMD (
      [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] stMDP[] arrayMD,
      int length
    );
    
    var x = new stMDP[100];
    RetrieveMD(x, 100);
    

    【讨论】:

      【解决方案3】:

      我让它工作了。我有没有想过让这件事变得更难。

      我重新阅读了“.NET 2.0 互操作性秘诀:一种问题解决方法”的第 2 章。

      这是有效的。

      在 C++ 方面,我删除了指针

      struct stMD 
      { 
      float Price; 
      unsigned int PriceDir; 
      unsigned int PriceDirCnt; 
      }; 
      
      // Global memory 
      #pragma data_seg (".IPC") 
      bool NewPoint = false; // Flag used to signal a new point. 
      static stMD aryMD [100] = {{0}}; 
      #pragma data_seg() 
      
      void __stdcall RetrieveMD (stMD LatestMD [100]) 
      { 
      memcpy(LatestMD, aryMD, sizeof(aryMD)); 
      }
      

      在 C# 方面,我删除了两个 (ref) 并添加了 [In, Out]

      [StructLayout(LayoutKind.Sequential)] 
      public struct stMD 
      { 
          public float Price; 
          public uint PriceDir; 
          public uint PriceDirCnt; 
      }; 
      
      public static stMD[] arrayMD = new stMD[100]; 
      
      [DllImport(@"Market.dll")] 
      public static extern void RetrieveMD([In, Out] stMD[] arrayMD); 
      
      RetrieveMD(arrayMD); 
      

      感谢所有提供帮助的人。

      【讨论】:

        猜你喜欢
        • 2014-01-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-17
        • 1970-01-01
        相关资源
        最近更新 更多