[英]Passing an struct array into C++ DLL from C#
我正在嘗試將結構數組傳遞到 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);
我的結構是 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());
}
再次感謝。 任何幫助將不勝感激,因為我認為這是一項簡單的任務,實際上讓我陷入困境!
您必須在 [DllImport] 屬性中使用 CallingConvention 屬性。 默認是 StdCall,這里需要 Cdecl,因為 C++ 聲明沒有使用 __stdcall。
如果int iSize
是元素中數組的大小(例如 data.Length),請嘗試使用MarshallAs.SizeParamIndex 。 這將告訴編組器在數據中應該有多少元素。
[DllImport("SocketAPI.dll")]
static extern int api_get_data(int iSize, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] buffer_node[] data);
帶有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:我剛剛注意到……你試過像這樣說[Out]
嗎?
[DllImport("SocketAPI.dll")]
static extern int api_get_data(int iSize, [Out] buffer_node[] data);
這可能會奏效,而不會像我上面所做的那樣痛苦。
旁注:除非您還更改對齊方式,否則說Size = 23
不會做任何事情,因為結構將被填充以達到默認對齊方式。
我必須將空數組從 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);
}
使用 [In, Out] 屬性作為 buffer_node[] 數據參數:
[DllImport("SocketAPI.dll")]
static extern int api_get_data(int iSize, [In, Out] buffer_node[] data);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.