Пытаюсь передать массив 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)
В комментарий не влезает моё сообщение, напишу ответом.
Так как строки при интеропе не используются, параметр 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[]
, сам это делает.
У вас 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));
До сих пор я считал что с# не умеет передавать масивы в с++. Код верный.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Не получается решить проблемуУ меня есть TextBlock, который показывает имя файла и расширение
Интересует вопрос формирования отчета в ExcelИмеется база данных
Распознавание речи в оффлайне не работает с русским языком,но работает со всем остальнымиВсе словари скачаны