#c# #c #.net-core #garbage-collection #interop
#c# #c #.net-core #сбор мусора #взаимодействие
Вопрос:
Я пытаюсь передать делегат из .NET core в качестве указателя на функцию в DLL с интерфейсом C.
Функция C принимает указатель на функцию. Это будет установлено как дескриптор функции обратного вызова внутри.
При попытке вызвать функцию обратного вызова из C DLL я получаю следующую ошибку.
«Фатальная ошибка. Система.AccessViolationException: попытка чтения или записи защищенной памяти. Это часто указывает на то, что другая память повреждена «.
Примечание: Исходный код DLL также можно редактировать. DLL и приложения .NET Core должны работать на разных платформах, отсюда и требование языка C вместо C .
Комментарии:
1. Никто не знает, что вы на самом деле пытались и как. Вы должны поделиться своей попыткой.
2. Не уверен, как вы ожидаете, что мы сообщим вам о проблеме в вашем коде, если мы ее не видим?
Ответ №1:
Итак, проблема в том, что у вас есть что-то вроде этого:
[DllImport("C.dll")]
public static extern int SetCallback(Action delegate);
public static extern int PerformAction();
public static extern int ClearCallback(Action delegate);
public void PerformSomeOperation()
{
SetCallback(HandleEvent);
for(int i = 0; i < 5; i ) {
PerformAction();
}
ClearCallback(HandleEvent);
}
private void HandlEvent()
{
}
Проблема в том, что делегат собрал мусор. Исправление этого заключается в том, чтобы сохранить делегат в рабочем состоянии:
public void PerformSomeOperation()
{
Action handler = (Action)HandleEvent;
SetCallback(handler);
for(int i = 0; i < 5; i ) {
PerformAction();
}
ClearCallback(handler);
}
Если область действия обработчика обратного вызова является глобальной, сохраните делегат в глобальной переменной и очистите его сразу после вызова ClearCallback
.
Если область действия обработчика обратного вызова является глобальной и обработчик никогда не освобождается, сохраните делегат в статической переменной и никогда не очищайте его.
Если в структуре кода используется менеджер обратного вызова, выделенный из кучи, который по-прежнему не очищает обработчик, это все равно можно сделать. Нам просто нужно поддерживать делегат в рабочем состоянии, пока мы не закончим его использовать:
[DllImport("C.dll")]
public static extern IntPtr SetupAction(Action delegate);
public static extern int PerformAction(IntPtr ctx);
public static extern int FreeAction(IntPtr ctx);
public void PerformSomeOperation()
{
Action handler = HandleEvent();
IntPtr ctx = IntPtr.Zero;
try {
ctx = SetupAction(handler);
for(int i = 0; i < 5; i ) {
PerformAction();
}
finally {
if (ctx != IntPtr.Zero) FreeAction(ctx);
GC.KeepAlive(handler);
};
}
private void HandlEvent()
{
}
Версия TL; DR:
Если обработчик является локальным для работы и имеет функцию clear, поместите делегат в переменную и используйте одну и ту же переменную как в set, так и в clear .
Если обработчик является локальным для работы и не имеет четкой функции, поместите делегат в переменную и используйте GC.KeepAlive, чтобы сохранить его активным до последнего места, где его можно использовать.
Если обработчик является глобальным и имеет функцию clear, сохраните делегат в статической переменной и очистите переменную делегата после вызова функции clear .
Если обработчик является глобальным и не имеет четкой функции, сохраните делегат в статической переменной и никогда не очищайте его.