将对象/结构的数组/列表从C#库传递到C ++ MFC应用程序

时间:2017-06-29 08:26:57

标签: c# c++ struct unmanaged dllexport

我试图从C ++ MFC应用程序调用的C#库(.net 4.5)传递一个对象或结构数组,但是我无法获得正确的数据。另一个困难是C ++应用程序不知道对象计数。

我已成功传递简单类型(int,string)以及带有以下代码的结构(使用UnmanagedExports 1.2.7):

C#

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 4)]
public struct ItemA
{
    // Size: 4
    public int Id;
    // Size: 100
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string Name;
    // Size: 2(4)
    public bool IsEnabled;
}

[DllExport(CallingConvention = CallingConvention.Cdecl)]
static public IntPtr GetItemA(int itemId)
{
    // Check structure size
    Debug.Assert(Marshal.SizeOf(typeof(ItemA)) == 108);

    // Get ItemA from a WS
    var itemA = client.GetItemsA().First(i => i.Id == itemId);

    // Convert ItemA to structure pointer
    IntPtr buf = Marshal.AllocHGlobal(Marshal.SizeOf(itemA));
    Marshal.StructureToPtr(itemA, buf, false);
    return buf;
}

C ++

#pragma pack(4)
typedef struct
{
    // Size: 4
    int Id;
    // Size: 100
    TCHAR Name[50];
    // Size: 2(4)
    bool IsEnabled;
} ItemA;

extern "C"
{
    ItemA* GetItemA(int itemId);
}

// (...)

void CMyAppDlg::OnBnClickedButtonGetItemA()
{
    // Check structure size
    static_assert(sizeof ItemA == 108, "Size does not match C# struct size");
    ItemA* structItemA = GetItemA(1);    
}

我到处搜索,但我找不到任何功能,以回应我的具体问题。你能帮忙为GetItemAList编写C#和C ++代码,它们可以将结构或其他类型的数组返回给C ++应用程序吗?

提前致谢!

Edit1:我更改了代码以解决打包/大小结构问题(64位编译)。

Edit2:我用#pragma pack替换手动对齐。

编辑3:我一直坚持使用GetItemAList。这是我调用c#后带有MemoryException的实际c#/ c ++代码。你能帮我解决这个问题吗?提前谢谢。

C#

[DllExport(CallingConvention = CallingConvention.Cdecl)]
static public void GetItemAList([In, Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct, SizeParamIndex = 1)] ref ItemA[] myArray, ref int length)
{
    // Get ItemAList from a WS
    var itemAArray = client.GetItemsAList().ToArray();

    // Set myArray values
    length = itemAArray.Length;
    for (int i = 0; i < length; i++)
    {
         myArray[i] = itemAArray[i];
    }
}

C ++

extern "C"
{
    void GetItemAList(ItemA*& myArray, int& length);
}

// (...)

void CMyAppDlg::OnBnClickedButtonGetItemA()
{
    ItemA* myArray = new ItemA[100];
    int length = 100;
    GetItemAList(myArray, length);   
}

1 个答案:

答案 0 :(得分:0)

我还没有做任何检查,但是你的C ++版本可能用4字节打包编译(假设你正在编译32位)。

在这种情况下,您将拥有C ++方面:

// Field,   Field size, Total size
// -------------------------------
// Id,               0,          4
// Name,            50,         54
// IsEnabled,        1,         55
// (padding)         1,         56

在这方面,你应该添加一个static_assert来验证你是否达到了预期的大小。

static_check(sizeof ItemA == 56, "Size does not match C# struct size")

在C#方面,您将拥有:

// Field,   Field size, Total size
// -------------------------------
// Id,               0,          4
// Name,            50,         54
// IsEnabled,        4,         58
// (no padding)      0,         58

如果bool为1个字节,则大小为55。在这两种情况下,它都与C ++方面的大小不匹配。

由于很难了解所有规则并且没有犯任何错误,并且确保在将来的维护中没有任何损坏,您应该始终在代码中添加验证。

在C#方面,典型的验证类似于:Debug.Assert(Marshal.Sizeof(typeof(ItemA)) == 58

您还可以验证某些字段的偏移量或大小,但通常如果您手动计算的大小在两侧都匹配,那么如果您使用1个字节的打包,则可能特别正确。