1

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

我能找到的最好的例子是Marshal C++ int array to 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;
}
4

2 回答 2

3

“调用者分配”是使代码可移植同时保持可维护性的唯一方法。您的代码不仅不会更改调用者的指针,而且 C# 代码也无法释放您分配的内存(malloc-ed 内存不会被垃圾回收清理)。

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

如果在生成数据之前无法获得大小,则让一个函数返回大小和指向内容的指针(int**在 C# 端它将是ref IntPtr)。第二个函数将该数据复制到 C# 数组并释放本机缓冲区。

于 2011-10-10T19:32:36.703 回答
2

您的问题是 的定义list,它确实需要是 a__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;
}
于 2011-10-10T19:32:08.460 回答