将结构体数组传递到 C++来自 C# 的 DLL
我试图将结构数组传递到 C++ DLL 中并遇到问题。我已经尝试了好几天了,但没有成功。我可以从 C++ 中获取数据,但当我尝试使用 .NET 获取结构数组时,我遇到了问题。
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);
我的 Struct 是 buffer_node,其定义为:
[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());
}
再次感谢。任何帮助将不胜感激,因为我认为这是一项简单的任务,但实际上却让我陷入了困境!
I'm trying to pass a struct array into a C++ DLL and running into issues. I've been trying to figure it out for several days with no avail. I can get the data fine from from C++, I just run into problems when I try to get the struct array using .NET.
The C++ prototype is:
static __declspec(dllexport) int SocketAPI::api_get_data(int iSize, buffer_node *data);
In my C# code, I defined the function as:
[DllImport("SocketAPI.dll")]
static extern int api_get_data(int iSize, buffer_node[] data);
My Struct is buffer_node which is defined as:
[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;
}
If tried the following Imports:
// 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);
My C# GetData program currently looks like this:
// 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());
}
Thank you again. Any help would be greatly appreciated, as what I though would be a simple task is really throwing me for a loop!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您必须使用 [DllImport] 属性中的 CallingConvention 属性。默认是StdCall,这里需要Cdecl,因为C++声明没有使用__stdcall。
You will have to use the CallingConvention property in the [DllImport] attribute. The default is StdCall, you need Cdecl here since the C++ declaration didn't used __stdcall.
对 buffer_node[] 数据参数使用 [In, Out] 属性:
Use [In, Out] attributes for buffer_node[] data parameter:
如果
int iSize
是数组元素的大小(例如 data.Length),请尝试使用 MarshallAs.SizeParamIndex。这将告诉编组器数据中应包含多少个元素。有关数组如何聚合的更多信息,请访问 MSDN。
If
int iSize
is the size of the array in elements (e.g. data.Length), try using MarshallAs.SizeParamIndex. That will tell the marshaller how many elements should be in data.More info on how arrays are mashalled at MSDN.
带有
ref
和out
的不起作用,因为它们传递的是指向引用的指针,而不是指向第一个元素的指针。编辑1:我刚刚注意到,你不能像现在一样传递数组——结构内的托管数组通常不会按照你想要的方式进行编组。当我想到一个解决方案时,我会写一个解决方案,但我认为你必须手动整理事情。
编辑 2:如果您能够使用不安全的代码,那么这应该可以解决问题:将
ByValArray
中的所有内容更改为fixed byte[]
,然后使用此代码:(您必须将声明更改为指向元素的指针。)
编辑 3:我刚刚注意到...您是否尝试过像这样说
[Out]
?这可能会起作用,而不需要像我上面所做的那样痛苦。
旁注:除非您还更改对齐方式,否则说
Size = 23
不会执行任何操作,因为结构将被填充以达到默认对齐方式。The ones with
ref
andout
don't work, because they pass a pointer to the reference, not a pointer to the first element.Edit 1: I just noticed, you can't pass arrays around like you're doing right now -- managed arrays inside structs don't usually get marshaled the way you want them. I'll write a solution when I think of one, but I think you're going to have to marshal things by hand.
Edit 2: If you're able to use unsafe code, then this should fix the problem: Change everything from a
ByValArray
to afixed byte[]
, then use this code:(You'll have to change the declaration to be a pointer to an element.)
Edit 3: I just noticed... have you tried saying
[Out]
like this?That might just work, without the pain of doing what I did above.
Side note: Saying
Size = 23
won't do anything unless you also change the alignment, because the structure will be padded to reach the default alignment.我遇到了同样的问题,必须将一个空数组从 C# 传递到 dll 中的 C 函数。然后,该函数将返回指向填充结构的数组的第一个元素的指针。
这是我声明外部函数的方式:
有问题的结构:
这是我调用该函数以及将 IntPtr 转换为我的结构的方式:
I had the same problem with having to pass an empty array from C# to a C function in a dll. The function would then return the pointer pointing to the first element of the array filled with structs.
This is how I declare the external function:
The struct in question:
This is how I call the function and how I cast the IntPtr to my struct: