Возвращает массив целых чисел из кроссплатформенной DLL - PullRequest
1 голос
/ 06 октября 2011

Я создал кроссплатформенную DLL на C ++, которая компилируется как в Windows, так и в Mac OSX. В Windows у меня есть приложение C #, которое вызывает библиотеку DLL с помощью P / Invoke, а в Mac OSX целевое приложение C вызывает библиотеку DLL. У меня есть простые функции, которые прекрасно работают, но мне нужна новая функция, которая возвращает массив целых чисел.

Лучший пример, который я могу найти - это Маршал 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;
}

Ответы [ 2 ]

3 голосов
/ 10 октября 2011

«Caller-allocates» - единственный способ сделать код переносимым, сохраняя его поддерживаемым.Ваш код не только не меняет указатель вызывающего, но и код C # не может освободить выделенную вами память (malloc -данная память не будет очищена сборщиком мусора).

При поискеразмер быстрый (не требует генерации всех выходных данных), просто добавьте вторую функцию, чтобы вернуть размер.

Если вы не можете получить размер, пока не сгенерируете данные, сделайте одну функциювернуть размер и указатель на содержимое (int**, на стороне C # это будет ref IntPtr).И вторая функция, которая копирует эти данные в массив C # и освобождает собственный буфер.

2 голосов
/ 10 октября 2011

Ваша проблема с определением list, оно действительно должно быть __int32**, чтобы передать назад адрес выделенного массива. Чтобы уловить трудности взаимодействия указателей на указатели, как насчет того, чтобы вместо этого вернуть адрес list или null, если он не работает:

__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;
}
...