Собираются делегаты обратного вызова?
Беседа с FMOD для разработки игр на С#, и я на мгновение попал в ловушку, и я не могу обойтись. Я хочу сделать некоторые ветвящиеся звуковые файлы и синхронизировать некоторые действия с геймплеем и так, поэтому я попытался добавить синхронизирующие точки к своим музыкальным трекам. Здесь код:
public class Music
{
private Sound music;
private Channel channel;
private IntPtr syncPtr;
public string File { get; private set; }
public Music(string file)
{
File = file;
}
public void Load()
{
music = new Sound();
Audio.System.createSound(File, MODE.HARDWARE, ref music);
}
public void Unload()
{
music.release();
}
public virtual void Play()
{
Audio.System.playSound(channel == null ? CHANNELINDEX.FREE : CHANNELINDEX.REUSE, music, false, ref channel);
music.addSyncPoint(500, TIMEUNIT.MS, "wooo", ref syncPtr);
channel.setCallback(channelCallback);
}
private RESULT channelCallback(IntPtr channelraw, CHANNEL_CALLBACKTYPE type, IntPtr commanddata1, IntPtr commanddata2)
{
if (type == CHANNEL_CALLBACKTYPE.SYNCPOINT)
Console.WriteLine("sync!");
return RESULT.OK;
}
}
И затем...
m = new Music(MUS_TUTORIAL); //m is static
m.Load();
m.Play();
Песня загружается и воспроизводится отлично... пока она не ударит, что добавила 500 точек синхронизации. На этом этапе VС# выплевывает следующую ошибку изнутри FMOD.EventSystem.update():
Обратный вызов был сделан на собранном делегатом мусора типа "Игра! FMOD.CHANNEL_CALLBACK:: Invoke. Это может привести к сбоям приложений, коррупции и потери данных. При передаче делегатам неуправляемого кода они должны быть управляемое приложение, пока не будет гарантировано, что они никогда не будут вызваны.
Итак, как-то FMOD проигрывает делегату, который я ему передал. Экземпляр Music, который содержит делегат, не был собран мусором - я сохраняю его в статической переменной на данный момент, но я пробовал со статическим методом тоже безрезультатно. Если я отключу CallbackOnCollectedDelegate MDA, ошибка станет пустым ссылкой, поэтому MDA не ошибается. Я предполагаю, что я просто не могу полностью понять, что здесь делает FMOD.
Есть ли у С# + FMOD гуру возможность увидеть мою ошибку?
Ответы
Ответ 1
channel.setCallback(channelCallback);
Это утверждение проблемы. FMod - неуправляемый код. Здесь вы создаете объект-делегат и передаете его неуправляемому коду. Проблема в том, что сборщик мусора не может отслеживать ссылки, хранящиеся в собственном коде. Следующая мусорная коллекция не найдет ссылок на объект и не соберет его. Kaboom, когда внутренний код выполняет обратный вызов.
Вам нужно сохранить ссылку самостоятельно, чтобы этого не произошло:
public class Music
{
private SomeDelegateType callback
//...
public Music(string file)
{
File = file;
callback = new SomeDelegateType(channelCallback);
}
public virtual void Play()
{
Audio.System.playSound(channel == null ? CHANNELINDEX.FREE : CHANNELINDEX.REUSE, music, false, ref channel);
music.addSyncPoint(500, TIMEUNIT.MS, "wooo", ref syncPtr);
channel.setCallback(callback);
}
Вам нужно найти фактический тип делегата из кода оболочки FMod, я просто догадался в "SomeDelegateType".
Ответ 2
У меня была аналогичная проблема между VB.NET и пользовательской С++ DLL. Исправлено благодаря @Hans. Я обязан этим сайтом много раз для всех проблем, с которыми мне пришлось пройти. Добавляя решение проблемы + в надежде, что он помогает другим видеть одно и то же решение в другом контексте.
Объявлено это (в модуле)
Public Delegate Sub CB_FUNC(ByVal x As Integer, ByVal y As Integer)
Public Declare Sub vidProc_cb_MouseClick Lib "C:\Users\.....\vidProc\product\vidProc.dll" (ByVal addr_update As CB_FUNC)
Первоначально:
Имел простой вызов в кнопке button_click:
vidProc_cb_MouseClick(AddressOf updateXY)
Я бы получил ошибку CallbackOnCollectedDelegate. Не сразу, но после взаимодействия с другими объектами в форме, а затем пытается вызвать обратный вызов (который в моем случае был щелчком мыши в окне OpenCV).
Исправлено:
1) Объявление (в классе формы, декларации)
Private addr_update As CB_FUNC
2) Определение addr_update при загрузке формы
addr_update = New CB_FUNC(AddressOf updateXY)
3) Вызов функции "set callback" с новым указателем (в кнопке button_click)
vidProc_cb_MouseClick(addr_update)
Я думаю, что понял @Hans и внедрил его правильно (я не могу воспроизвести ошибку). Надеюсь, это поможет кому-то.