麻烦编组从C ++到C#的结构数据数组

时间:2012-10-24 16:22:56

标签: c# c++ arrays structure marshalling

我已经搜索了几天,并尝试了我能找到的所有内容,但仍无法使其发挥作用。

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

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

由于

这是我正在尝试的。 在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);

3 个答案:

答案 0 :(得分:2)

问题是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);

答案 1 :(得分:0)

我认为,你的问题是你试图通过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# - &gt; 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);

答案 2 :(得分:0)

我得到了它的工作。我是不是想要让它变得更难。

我重读了“.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)s并添加了[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); 

感谢所有提供帮助的人。