为什么在编组后,只有数组的一个元素包含一个值?

时间:2014-03-19 13:19:21

标签: c# c++ pinvoke

我从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);

2 个答案:

答案 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属性时才能正确读取并转换为字符串。

如果您决定以这种方式进行此操作,则需要在项目的构建选项下启用不安全的代码。