C# 将结构数组传递到C++;来自C的DLL#

C# 将结构数组传递到C++;来自C的DLL#,c#,c++,arrays,dll,struct,C#,C++,Arrays,Dll,Struct,我尝试将结构数组传递到C++ DLL中,并运行到问题中。我已经想了好几天了,但都没有结果。我可以从C++获得数据,当我尝试使用.NET.获取结构数组时,我遇到了一些问题。 < > C++原型是: static __declspec(dllexport) int SocketAPI::api_get_data(int iSize, buffer_node *data); 在我的C#代码中,我将函数定义为: [DllImport("SocketAPI.dll")] static extern i

我尝试将结构数组传递到C++ DLL中,并运行到问题中。我已经想了好几天了,但都没有结果。我可以从C++获得数据,当我尝试使用.NET.</P>获取结构数组时,我遇到了一些问题。 < > C++原型是:

static __declspec(dllexport) int SocketAPI::api_get_data(int iSize, buffer_node *data); 
在我的C#代码中,我将函数定义为:

[DllImport("SocketAPI.dll")]
static extern int api_get_data(int iSize, buffer_node[] data);
我的结构是缓冲区\节点,定义为:

[StructLayout(LayoutKind.Sequential, Size = 23), Serializable]
public struct header
{
    // HEADER
    public UInt16 h_type; 
    public UInt32 frame_num;  
    public UInt16 count_1pps;   
    public byte data_options;   
    public byte project_type;   
    public byte tile_num;     
    public byte tile_set;          
    public byte total_rows;     
    public byte total_cols;      
    public byte num_rows;         
    public byte num_cols;       
    public byte first_row;        
    public byte first_col;      
    public UInt16 num_sensors;      
    public UInt16 num_data_bytes; 
    public byte h_checksum;
}

[StructLayout(LayoutKind.Sequential, Size = 25), Serializable]
public struct footer
{
    // FOOTER
    public UInt16 f_type;  
    public byte ts_len;           
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
    public byte[] ts_array;
    public byte frame_status;
    public byte f_checksum;     
}

[StructLayout(LayoutKind.Sequential, Size = 51), Serializable]
public struct buffer_node
{
    // HEADER
    public header data_header;

    // DATA
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public byte[] data;

    // FOOTER
    public footer data_footer;
}
如果尝试了以下导入:

// See buffer, but everything is 0 - ie. not being populated
unsafe static extern int api_get_data(int iSize, buffer_node[] data); 

// fails somewhere in the API
static extern int api_get_data(int iSize, out buffer_node[] data); 

static extern int api_get_data(int iSize, ref buffer_node[] data);
我的C#GetData程序目前看起来如下所示:

// Get current data size
int iSize = api_is_data_available();

// Create buffer to hold the data
buffer_node[] buf_data = new buffer_node[iSize];

for (int i = 0; i < iSize; i++)
{
    buf_data[i].data = new byte[3];
    buf_data[i].data_footer.ts_array = new byte[20];
}

// Get the data
//int iStructSize = Marshal.SizeOf(buf_data[0]);
//IntPtr bufNodePtr = IntPtr.Zero;
//IntPtr buffer = Marshal.AllocHGlobal(iStructSize * iSize);
//api_get_data(iSize, buffer);
//for (int i = 0; i < iSize; i++)
//{
//    IntPtr ptr = new IntPtr(buffer.ToInt64() + iStructSize * i);
//    buf_data[i] = (buffer_node)Marshal.PtrToStructure(ptr, typeof(buffer_node));
//}

//api_get_data(iSize, buf_data); // See buffer, but everything is 0 - ie. not being populated
// api_get_data(iSize, out buf_data); // fails no error
api_get_data(iSize, ref buf_data); // fails no error
// api_get_data(iSize, ref buf_data);

// Print the data
for (int i = 0; i < iSize; i++)
{
    StringBuilder sb = new StringBuilder();
    sb.Append("Tile Number: " + Convert.ToString(buf_data[i].data_header.tile_num));
    AppendTextBox(sb.ToString());
}
//获取当前数据大小
int iSize=api_是_数据_可用();
//创建缓冲区以保存数据
buffer_node[]buf_data=新的buffer_node[iSize];
for(int i=0;i

再次感谢你。任何帮助都将不胜感激,因为我认为这将是一项简单的任务,这真的让我陷入了困境

带有
ref
out
的指针不起作用,因为它们传递的是指向引用的指针,而不是指向第一个元素的指针


编辑1:我刚刚注意到,不能像现在这样传递数组——结构中的托管数组通常不会按您希望的方式进行封送。当我想到一个解决方案时,我会写一个解决方案,但我认为你必须手工整理东西


编辑2:如果您能够使用不安全的代码,那么这应该可以解决问题:将所有内容从
ByValArray
更改为
fixed byte[]
,然后使用此代码:

[StructLayout(LayoutKind.Sequential, Size = 23), Serializable]
public struct header
{
    // HEADER
    public UInt16 h_type; 
    public UInt32 frame_num;  
    public UInt16 count_1pps;   
    public byte data_options;   
    public byte project_type;   
    public byte tile_num;     
    public byte tile_set;          
    public byte total_rows;     
    public byte total_cols;      
    public byte num_rows;         
    public byte num_cols;       
    public byte first_row;        
    public byte first_col;      
    public UInt16 num_sensors;      
    public UInt16 num_data_bytes; 
    public byte h_checksum;
}

[StructLayout(LayoutKind.Sequential, Size = 25), Serializable]
public struct footer
{
    // FOOTER
    public UInt16 f_type;  
    public byte ts_len;           
    public unsafe fixed byte ts_array[20];
    public byte frame_status;
    public byte f_checksum;     
}

[StructLayout(LayoutKind.Sequential, Size = 51), Serializable]
public struct buffer_node
{
    // HEADER
    public header data_header;

    // DATA
    public unsafe fixed byte data[3];

    // FOOTER
    public footer data_footer;
}

unsafe static extern int api_get_data(int iSize, buffer_node* pData);


//...

// Get current data size
int iSize = api_is_data_available();

// Create buffer to hold the data
buffer_node[] buf_data = new buffer_node[iSize];

unsafe
{
    fixed (buffer_node* pBufData = buf_data)
    {
        api_get_data(iSize, pBufData); // fails no error
    }
}
(必须将声明更改为指向元素的指针。)


编辑3:我刚刚注意到。。。你试过这样说吗

[DllImport("SocketAPI.dll")]
static extern int api_get_data(int iSize, [Out] buffer_node[] data);
这可能只是工作,没有痛苦的做我上面所做的


旁注:除非同时更改对齐方式,否则说
Size=23
不会起任何作用,因为结构将被填充以达到默认对齐方式。

必须在[dlliport]属性中使用CallingConvention属性。默认是STDCLAK,这里需要CDDEL,因为C++声明没有使用“yStdCald.< /P> > P”>如果代码> int IS/SCOR>是元素(例如,数据,长度)中数组的大小,请尝试使用。这将告诉封送员数据中应该包含多少元素

[DllImport("SocketAPI.dll")]
static extern int api_get_data(int iSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)]  buffer_node[] data);

有关数组的详细信息。

我也遇到过同样的问题,必须将空数组从C#传递到dll中的C函数。然后,函数将返回指向填充了结构的数组的第一个元素的指针

以下是我声明外部函数的方式:

[DllImport(LIB_NAME, CallingConvention = CallingConvention.StdCall, EntryPoint = "getData")]
unsafe extern void getData(IntPtr data, ref UInt32 dataLen);
有关结构:

[StructLayout(LayoutKind.Sequential)]
internal struct DataC
{
    internal UInt16 xRes, yRes;
    internal fixed float rot[9];
}
这是我调用函数的方式,也是我将IntPtr强制转换为结构的方式:

unsafe
{
    UInt32 dataLen = 10;
    IntPtr dataPtr = Marshal.AllocHGlobal((int)dataLen * Marshal.SizeOf(typeof(DataC)));
    getData(dataPtr, ref dataLen);
    // check here for null, obviously
    DataC* dataArr = (DataC*)dataPtr;
    for (int i = 0; i < dataLen; i++)
    {
        DataC data = dataArr[i];
        // I fill a managed class/struct with the unmanaged data and add it to a List or whatever
        result.Add(new Data(data->xRes, data->yRes, data->rot[0], ...));
    }
    // As we have the data in managed memory now, we free the allocated space
    Marshal.FreeHGlobal(dataPtr);
}
不安全
{
UInt32数据长度=10;
IntPtr dataPtr=Marshal.AllocHGlobal((int)dataLen*Marshal.SizeOf(typeof(DataC));
getData(dataPtr,ref dataLen);
//显然,在这里检查空值
DataC*dataArr=(DataC*)dataPtr;
对于(int i=0;ixRes,数据->yRes,数据->rot[0],…);
}
//由于现在数据在托管内存中,我们释放了分配的空间
弗里赫全球元帅(dataPtr);
}

为缓冲区节点[]数据参数使用[In,Out]属性:

[DllImport("SocketAPI.dll")]
static extern int api_get_data(int iSize, [In, Out] buffer_node[] data);

你好谢谢你的回复。我的偏好是使用安全代码。然而,在这一点上,我只是想让它工作。我不明白如何实现您在“编辑2”中编写的内容。谢谢。嗯。。。“[Out]”可能已经完成了。我得再做些测试。这是一个很难在谷歌上搜索的问题。什么意思?它与out有什么区别?看起来第一个元素正确返回,之后有些东西误入歧途了。我会处理一会儿,看看我是不是在哪里弄错了尺寸。谢谢做了一些彻底的检查。缓冲区节点数组中的第一个元素是完整的。其余元素已损坏。关于
[Out]
属性:这意味着您希望将数据从C代码封送回原始数组。(运行时不会传递直接指针;它会在一个或两个方向上复制数据。)如果您说
out
,这意味着您将返回一个指向新数组的指针,这里的情况并非如此。关于大小:如果确实需要奇数大小,则必须使用
Pack
属性:
[StructLayout(LayoutKind.Sequential,size=23,Pack=1)]
;否则它就做不到你想要的。嗨。很抱歉我一点也不明白。请你解释一下。Google+dllimportattribute+callingconvention。第三次击中看起来不错。这实际上是正确的答案。我有一个类似的问题,它解决了这个解决方案。在本例中,添加:
[DllImport(“SocketAPI.dll”,CallingConvention=CallingConvention.Cdecl)]
这使数组再次充满了0。我将在您提供的链接上阅读更多内容。谢谢