#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)); До сих пор я считал что с# не умеет передавать масивы в с++. Код верный.
Комментариев нет:
Отправить комментарий