Извлечение массива из C++ (неуправляемый код) в форму C Sharp (управляемый)

У меня ниже реализация на С++ (создана одна и та же DLL)

double *getData()
{
    double *eyeTrackData = new double[10];
    const unique_ptr<Fove::IFVRHeadset> headset{ Fove::GetFVRHeadset() };

    CheckError(headset->Initialise(Fove::EFVR_ClientCapabilities::Gaze), 
"Initialise");

    Fove::SFVR_GazeVector leftGaze, rightGaze;
    const Fove::EFVR_ErrorCode error = headset->GetGazeVectors(&leftGaze, 
    &rightGaze);


    // Check for error
    switch (error)
    {

    case Fove::EFVR_ErrorCode::None:
        eyeTrackData[0] = (double)leftGaze.vector.x;
        eyeTrackData[1] = (double)leftGaze.vector.y;
        eyeTrackData[2] = (double)rightGaze.vector.x;
        eyeTrackData[3] = (double)rightGaze.vector.y;
        break;


    default:
        // Less common errors are simply logged with their numeric value
        cerr << "Error #" << EnumToUnderlyingValue(error) << endl;
        break;
    }

    return eyeTrackData;
}

Я включил

extern "C"
{
    __declspec(dllexport) double *getData();
}

в заголовочном файле.

Я пытаюсь получить это в до-диез.

[DllImport("C:\\Users\\BME 320 - Section 1\\Documents\\Visual Studio 2015\\Projects\\EyeTrackDll\\x64\\Debug\\EyeTrackDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr eyeData();

Но я не знаю, как получить массив внутри события buttonClick.

Я ценю любую помощь в этом.


person user527248    schedule 13.06.2018    source источник
comment
Прочитайте массив с помощью Marshal.Copy. Но как вы планируете освободить массив? И как вызывающая сторона узнает длину массива? Если длина фиксирована, то лучше выделить вызывающей стороне.   -  person David Heffernan    schedule 13.06.2018


Ответы (1)


Следуя советам @DavidHeffernan, я постараюсь дать вам пример того, как достичь вашей цели. Пожалуйста, примите часть C# как псевдокод, так как я не эксперт в этом языке.

Я бы использовал структуру, как было сказано ранее, просто чтобы прояснить ситуацию, и я считаю хорошей практикой дать некоторую защиту типа и возможность добавить в структуру атрибут «размер», если точно не знаю, сколько двойников будет быть. В вашем случае, как всегда 4, не нужно.

Функция в библиотеке С++:

void getData(double* eyeTrackData)
{
    const unique_ptr<Fove::IFVRHeadset> headset{ Fove::GetFVRHeadset() };

    CheckError(headset->Initialise(Fove::EFVR_ClientCapabilities::Gaze), "Initialise");

    Fove::SFVR_GazeVector leftGaze, rightGaze;
    const Fove::EFVR_ErrorCode error = headset->GetGazeVectors(&leftGaze, &rightGaze);


    // Check for error
    switch (error)
    {

    case Fove::EFVR_ErrorCode::None:
        eyeTrackData[0] = (double)leftGaze.vector.x;
        eyeTrackData[1] = (double)leftGaze.vector.y;
        eyeTrackData[2] = (double)rightGaze.vector.x;
        eyeTrackData[3] = (double)rightGaze.vector.y;
        break;

    default:
        // Less common errors are simply logged with their numeric value
        cerr << "Error #" << EnumToUnderlyingValue(error) << endl;
        break;
    }
}

Сторона С#:

[DllImport("C:\\Users\\BME 320 - Section 1\\Documents\\Visual Studio 2015\\Projects\\EyeTrackDll\\x64\\Debug\\EyeTrackDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void eyeData(IntPtr); 

private void button1_Click(object sender, EventArgs e) 
{ 
    try 
    { 
        IntPtr ipr = Marshal.AllocHGlobal(4); // Memory blob to pass to the DLL
        eyeData(ipr);
        double[] eyeTrackData = new double[4]; // Your C# data
        Marshal.Copy(ipr, eyeTrackData, 0, 4); // Convert?
    }
    finally 
    { 
        Marshal.FreeHGlobal(ipr); 
    }
} 

Еще раз извините за мой "плохой C#" xD. Надеюсь это поможет.

person LuisGP    schedule 13.06.2018
comment
getData() остается прежним? как насчет получения их в конце С#? - person user527248; 13.06.2018
comment
Решение LuisGP работает, но оно приводит к утечке памяти, потому что getDat() возвращает массив двойников, выделенных через new, и .Net не знает, как его освободить. Либо измените метод, чтобы использовать двойной буфер, предоставленный вызывающей стороной, либо оставайтесь с возвратом IntPtr, предоставьте дополнительный метод deleteData() для освобождения intptr и используйте System.Runtime.Interop.Marshal.PtrToStructure. - person wech; 13.06.2018
comment
Вы должны использовать структуру вместо double*, конечно. В стороне С# я не эксперт. Как мы говорим, это приведет к утечке памяти, если вы не предоставите метод deleteData() для управления освобождением памяти каким-либо другим способом. - person LuisGP; 13.06.2018
comment
Нет необходимости использовать структуру вообще. Бред какой то. ИнтПтр в порядке. Вы используете класс Marshal для чтения неуправляемой памяти. Deallocator должен быть экспортирован в. Или выделение на общей куче. - person David Heffernan; 13.06.2018
comment
Здесь я застрял на конце до-диез: private void button1_Click(object sender, EventArgs e) { IntPtr ipr = eyeData(); double[] eyeTrackData = new double[10]; интервал j = 0; eyeTrackData[0] = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(ipr, 11*j)); Последняя строка выдает ошибку. Кроме того, я передаю массив двойного типа, так что мне с этим делать? - person user527248; 13.06.2018
comment
Массив двойных значений не является строкой - person David Heffernan; 13.06.2018
comment
@DavidHeffernan, но мне нужно, чтобы данные, которые отправляются, были двойными. - person user527248; 14.06.2018
comment
Верно. Так почему вы пытаетесь преобразовать его в текст? Вместо того, чтобы общаться в комментариях к этому плохому ответу, как насчет решения вопросов в моем комментарии к вопросу? - person David Heffernan; 14.06.2018
comment
Длина массива, возвращаемого из неуправляемого кода, всегда будет равна четырем. Я хочу преобразовать его в текст, потому что я хочу отобразить его в текстовом поле. - person user527248; 14.06.2018
comment
ХОРОШО. Попросите вызывающую сторону выделить его и передать массив в качестве параметра, который заполняет вызывающая сторона. - person David Heffernan; 14.06.2018
comment
[DllImport(C:\\Users\\BME 320 - Section 1\\Documents\\Visual Studio 2015\\Projects\\EyeTrackDll\\x64\\Debug\\EyeTrackDll.dll, CallingConvention = CallingConvention.Cdecl)] public static внешний IntPtr eyeData(); private void button1_Click (отправитель объекта, EventArgs e) { IntPtr ipr = eyeData(); попробуйте { double[] eyeTrackData = new double[4]; Marshal.Copy(ipr, eyeTrackData, 0, 4); наконец { Marshal.FreeHGlobal (ipr); } - person user527248; 14.06.2018
comment
Я написал это и получил исключение EntryPoinNotFound. Любые предложения @DavidHeffernan - person user527248; 14.06.2018
comment
Мне надоело делать это в комментариях к ответу. Я выхожу. Вы продолжаете игнорировать мои предложения, так что это просто пустая трата моего времени. - person David Heffernan; 14.06.2018
comment
Я обновил свой ответ, чтобы попытаться обобщить все советы Дэвида Хеффернана. - person LuisGP; 14.06.2018
comment
@LuisGP ваше решение не работает. данные ока(IPR); ошибка: невозможно преобразовать из System.IntPtr[] в System.IntPtr. Кроме того, Marshal.Copy(ipr, eyeTrackData, 0, 4); Невозможно преобразовать из double[] в int - person user527248; 14.06.2018
comment
Я отредактировал свой ответ, добавив немного больше знаний о IntPtr, которые я только что погуглил. Почему нет? - person LuisGP; 14.06.2018
comment
Я это сделал. Теперь я застрял с EntryPointNotFoundException, который я много гуглил и придумал 2 исправления, которые не сработали. - person user527248; 14.06.2018
comment
Вы видели эту ссылку? Возможно, вам стоит попробовать DependencyWalker. Похоже, вы неправильно экспортируете/импортируете функцию в dll. - person LuisGP; 14.06.2018
comment
Это можно упростить с помощью (в C#): void eyeData(double[] array);, а затем double[] eyeTrackData = new double[4]; eyeData(eyeTrackData);`. Существует автозакрепление массивов, а массивы примитивного типа не маршалируются, а просто передаются как ссылка. - person xanatos; 15.06.2018