Собираются делегаты обратного вызова?

Беседа с 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 и внедрил его правильно (я не могу воспроизвести ошибку). Надеюсь, это поможет кому-то.