从跨平台DLL返回整数数组

本文关键字:整数 数组 返回 DLL 跨平台 | 更新日期: 2023-09-27 18:10:41

我用c++创建了一个跨平台的DLL,可以在Windows和Mac OSX上编译。在Windows上,我有一个使用P/Invoke调用DLL的c#应用程序,在Mac OSX上,一个objective - C应用程序调用DLL。我有一个简单的函数,工作得很好,但我需要一个新的函数,返回一个整数数组。

我能找到的最好的例子是在Marshal c++ int数组到c#,我能够使它工作。但是,我想修改这个示例,将整数数组作为引用参数传递回去。数组的大小必须在运行时设置。

这是我试过的。pSize返回正确,但列表为空。

在非托管c++中:

bool GetList(__int32* list, __int32* pSize)
{
    // Some dummy data
    vector<int> ret;
    ret.push_back(5);
    ret.push_back(6);
    list = (__int32*)malloc(ret.size());
    for (unsigned int i = 0; i < ret.size(); i++)
    {
            list[i] = ret.at(i);
    }
    *pSize = ret.size();
    return true;
}
在c#:

[DllImport(@"MyDll.dll",
    CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
    public static extern bool GetList(out IntPtr arrayPtr, out int size);

public static int[] GetList() {
    IntPtr arrayValue = IntPtr.Zero;
    int size = 0;
    bool b = GetFrames(out arrayValue, out size);
    // arrayValue is 0 here
    int[] result = new int[size];
    Marshal.Copy(arrayValue, result, 0, size);
    return result;
}

从跨平台DLL返回整数数组

" caller -allocate "是使代码可移植同时保持可维护性的唯一方法。你的代码不仅没有改变调用者的指针,而且c#代码没有办法释放你分配的内存(malloc -ed内存不会被垃圾收集清理)。

如果查找大小很快(不需要生成所有输出数据),只需添加第二个函数来返回大小。

如果你在生成数据之前不能得到大小,那么让一个函数返回大小和指向内容的指针(int**,在c#方面它将是ref IntPtr)。第二个函数将数据复制到c#数组并释放本机缓冲区。

您的问题是list的定义,它确实需要是__int32**,以便将传递回分配的数组的地址。为了轻松解决指针对指针的互操作困难,如果失败,您可以返回listnull的地址:

__int32* GetList(__int32* pSize)
{
    // Some dummy data
    vector<int> ret;
    ret.push_back(5);
    ret.push_back(6);
    // per @David's catch, you'll need to allocate the right amount
    __int32* list = (__int32*)malloc(ret.size() * sizeof(__int32));
    for (unsigned int i = 0; i < ret.size(); i++)
    {
            list[i] = ret.at(i);
    }
    *pSize = ret.size();
    return list;
}
void RemoveList(__int32* list)
{
    free(list);
}

加上对c#的适当修改:

[DllImport(@"MyDll.dll",
 CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GetList(out int size);
[DllImport(@"MyDll.dll",
 CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
private static extern void RemoveList(IntPtr array);
public static int[] GetList()
{
    int[] result = null;
    int size;
    IntPtr arrayValue = IntPtr.Zero;
    try
    {
        arrayValue = GetList(out size);
        if (arrayValue != IntPtr.Zero)
        {
            result = new int[size];
            Marshal.Copy(arrayValue, result, 0, size);
        }
    }
    finally
    {
        // don't forget to free the list
        RemoveList(arrayValue);
    }
    return result;
}