Страницы

Поиск по вопросам

воскресенье, 5 января 2020 г.

Передача Int массива из C# в Dll на C++

#c_sharp #cpp #net #dll #interop


Пытаюсь передать массив Int из C# в C++. Выскакивает ошибка:


  Попытка чтения или записи в защищенную память. Это часто свидетельствует о том,
что другая память повреждена.


[DllImport("FindProject.dll", CharSet = CharSet.Unicode)]
public static extern bool Find(int count, [In, Out] int[] first_v, [In, Out] int[]
second_v);


int[] fv = new int[Convert.ToInt32(count_rec)];
int[] sv = new int[Convert.ToInt32(count_rec)];

sqlExpression = "SELECT FIRST_VERTEX, SECOND_VERTEX FROM Edges";
using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();
    SqlCommand command = new SqlCommand(sqlExpression, connection);
    SqlDataReader reader = command.ExecuteReader();

    if (reader.HasRows) // проверка на наличие данных
    {
        int i = 0;
        while (reader.Read())
        {
            int id_FVvertex = reader.GetInt32(0);
            int id_SVvertex = reader.GetInt32(1);

            fv[i] = id_FVvertex;
            sv[i] = id_SVvertex;

            i++;
        }

        Find(Convert.ToInt32(count_rec), fv, sv);
    }
}


Данные для массива я получаю из заброса к базе данных. 
Если явно указывать константное значение для элементов массива, то все работает. 
Подскажите, как исправить данную проблему?

Строка объявления функции в Dll: 

extern "C" _declspec(dllexport) bool __stdcall Find(int count, int *first_v, int
*second_v)

    


Ответы

Ответ 1



В комментарий не влезает моё сообщение, напишу ответом. Так как строки при интеропе не используются, параметр CharSet не нужен. Хотя он ничего и не портит. Соглашение о вызове по умолчанию равно CallingConvention.StdCall - можно не добавлять. Возвращаемый тип bool нужно маршалировать с указанием следующего атрибута: [return: MarshalAs(UnmanagedType.Bool)] Массивы следует помечать атрибутом [MarshalAs(UnmanagedType.LPArray)] В итоге, объявление функции может выглядеть так: [DllImport("FindProject.dll")] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool Find( int count, [MarshalAs(UnmanagedType.LPArray), In, Out] int[] first_v, [MarshalAs(UnmanagedType.LPArray), In, Out] int[] second_v ); Я не уверен, нужно ли массив преобразовывать в указатель типа IntPtr. Вроде бы маршалер, когда встречает int[], сам это делает.

Ответ 2



У вас fv - используется чисто на запись, и нигде на чтение. Вероятнее всего GC его во время вызова - просто убирает как мусор, считая что далее массив не используется (константные значения хранятся в отдельном месте памяти, и их уборщик не убирает). Самым простым решением будет сказать уборщику, что массив вам нужен, до вызова Find (лучше сразу после new) GC.KeepAlive(fv); GC.KeepAlive(sv); Чуть по сложнее, можно создать ссылку на массив, состояние ссылки Pinned (в англ pining называется) не даёт среде уничтожить обьект. GCHandle myArrayHandle1 = GCHandle.Alloc(fv,GCHandleType.Pinned); GCHandle myArrayHandle2 = GCHandle.Alloc(sv,GCHandleType.Pinned); Find(Convert.ToInt32(count_rec), fv, sv); myArrayHandle1.Free(); myArrayHandle2.Free(); Ранее тут был пост Можно попробовать вариант с открытыми адресами... и дизассемблером на моменте ошибки проследить ссылки. Обьявим ф-цию [DllImport("FindProject.dll")] public static extern bool Find(int count, IntPtr first_v, IntPtr second_v); А вызов будет через маршалинг, таким Find(Convert.ToInt32(count_rec), Marshal.UnsafeAddrOfPinnedArrayElement(fv,0), Marshal.UnsafeAddrOfPinnedArrayElement(sv,0)); До сих пор я считал что с# не умеет передавать масивы в с++. Код верный.

Комментариев нет:

Отправить комментарий