我从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);
答案 0 :(得分:1)
这对于编组是一个有点尴尬的功能。非托管代码分配数组并将指向该数组的指针返回给调用者。因此签名中的双指针。你不能使用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
);
答案 1 :(得分:0)
对字符串进行编组比最初看起来更烦人。将字符串编组到固定的字节缓冲区中,然后像这样构造字符串可能更容易:
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
属性时才能正确读取并转换为字符串。
如果您决定以这种方式进行此操作,则需要在项目的构建选项下启用不安全的代码。