为什么在编组之后,数组中只有一个元素包含值
本文关键字:有一个 元素 包含值 数组 之后 为什么 | 更新日期: 2023-09-27 17:50:53
我正在从我的c#代码调用c++函数。我正在使用编组,但是当从c++代码返回时,在我的c#代码中只有一个元素填充了这个数组。
我的c++结构体:
typedef struct DEV_SUB_STATE_ITEM_s
{
char err_text[NAME_MAX_LENGTH];
uint32_t state;
char obj_name[NAME_MAX_LENGTH];
char name[NAME_MAX_LENGTH];
} DEV_SUB_STATE_ITEM_t;
我的c#结构:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DEVICE_Sub_State_Item
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public String err_text;
public UInt32 state;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public String obj_name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public String name;
}
我的c++函数原型:
int COMSpClient::GetSubSlotList (UINT32 obj_rid, DEV_SUB_STATE_ITEM_t** subSlotItems);
我的c#函数原型:
[DllImport(@"xxx_OMSpClient.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?OMSpClient_GetSubSlotList@@YAHPAXHPAPAUDEV_SUB_STATE_ITEM_s@@@Z", CharSet = CharSet.Auto)]
public static unsafe extern Int32 GetSubSlotList(Int32 p_hHandle, UInt32 obj_rid,[MarshalAs(UnmanagedType.LPArray)] ref DEVICE_Sub_State_Item[] sub_slot_items);
在c#中的用法:
OMSpClientWrapper.DEVICE_Sub_State_Item[] sub_slots = new OMSpClientWrapper.DEVICE_Sub_State_Item[5];
// TODO : load subordinate list!!!
OMSpClientWrapper.GetSubSlotList(this.omsp_client_handle, MyDevice.DeviceRID, ref sub_slots);
这是一个有点笨拙的封送函数。非托管代码分配数组并将指向该数组的指针返回给调用方。因此在签名中使用双指针。您不能使用p/invoke自动封送。
您将需要使用IntPtr
,作为out
参数传递,然后自己执行其余的封送。
[DllImport(...)]
public static extern int GetSubSlotList(
IntPtr p_hHandle,
uint obj_rid,
out IntPtr sub_slot_items
);
此时,sub_slot_items
指向数组的第一个元素。然后,您需要使用Marshal.PtrToStructure
来读出每一项,并在读取过程中增加点。
并且您可能需要回调非托管代码以要求它释放内存。
当然,这很乱。如果您可以控制接口,那么更好的设计是让调用者分配数组。代码看起来像这样:
int COMSpClient::GetSubSlotList(
UINT32 obj_rid,
DEV_SUB_STATE_ITEM_t subSlotItems[]
);
你也可能希望传递数组的长度,除非有其他原因使它为双方都知道。
在c#方面,代码将是:
[DllImport(...)]
public static extern int GetSubSlotList(
IntPtr p_hHandle,
uint obj_rid,
[Out] DEVICE_Sub_State_Item[] sub_slot_items
);
编组到字符串比它最初看起来要烦人得多。将字符串封送到固定字节缓冲区中可能更容易,然后像这样构造字符串:
public unsafe struct DEVICE_Sub_State_Item
{
public fixed byte err_text[50];
public UInt32 state;
public fixed byte obj_name[50];
public fixed byte name[50];
public string ErrorText
{
get
{
byte[] buffer = new byte[50];
fixed (byte* b = err_text)
Marshal.Copy(new IntPtr(b), buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
}
}
}
实际的错误文本将作为结构体中的指针保存,只有在调用其ErrorText
属性时才能正确读取并转换为字符串。
如果您决定这样做,您将需要在项目的构建选项下启用不安全代码。