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