Коллбэк делегатов собирают? - PullRequest
       17

Коллбэк делегатов собирают?

17 голосов
/ 05 сентября 2011

Я возился с FMOD для разработки игр на C #, и я рано наткнулся на загадку, которую никак не могу обойти. Я хочу сделать некоторые разветвленные аудиофайлы и синхронизировать некоторые игровые действия с ритмами и тому подобным, поэтому я попытался добавить точки синхронизации к своим музыкальным трекам. Вот код:

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-миллисекундной синхронизации, которую я добавил На этом этапе VC # выдает следующую ошибку из FMOD.EventSystem.update ():

Был выполнен обратный вызов для делегата типа «сборщик мусора» "Игра! FMOD.CHANNEL_CALLBACK :: Invoke. Это может вызвать сбои приложения, повреждение и потери данных. При передаче делегатов в неуправляемый код они должны поддерживаться управляемым приложением, пока не будет гарантировано, что они никогда не будут вызываться.

Так что, как-то FMOD теряет след делегата, которого я пропустил. Экземпляр Music, который содержит делегат, не был собран сборщиком мусора - я сейчас храню его в статической переменной - но я тоже пытался использовать статический метод, но безрезультатно. Если я отключаю CallbackOnCollectedDelegate MDA, ошибка становится исключением из нулевой ссылки, поэтому MDA не ошибается. Полагаю, я просто не совсем понимаю, что здесь делает FMOD.

Могут ли какие-нибудь гуру C # + FMOD увидеть мою ошибку?

Ответы [ 2 ]

28 голосов
/ 05 сентября 2011
    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".

0 голосов
/ 19 февраля 2015

У меня была похожая проблема между VB.NET и пользовательской C ++ DLL.Исправлено благодаря @Hans.Я обязан этому сайту много раз за все проблемы, с которыми он меня пережил.Добавление моей проблемы + решение в надежде, что это поможет другим увидеть то же решение в другом контексте.

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

...