收集回调代表?

Mat*_*att 18 c# delegates garbage-collection fmod

一直在忙着用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后...

m = new Music(MUS_TUTORIAL);  //m is static
m.Load();
m.Play();
Run Code Online (Sandbox Code Playgroud)

这首歌加载并播放正常...直到它达到我添加的500毫秒同步点.此时,VC#从FMOD.EventSystem.update()中吐出以下错误:

在"Game!FMOD.CHANNEL_CALLBACK :: Invoke"类型的垃圾收集委托上进行了回调.这可能会导致应用程序崩溃,损坏和数据丢失.将委托传递给非托管代码时,托管应用程序必须将它们保持活动状态,直到确保它们永远不会被调用.

所以不知何故FMOD正在失去我通过它的代表的踪迹.持有委托的Music实例并没有被垃圾收集 - 我现在将它存储在一个静态变量中 - 但我尝试使用静态方法也无济于事.如果我禁用CallbackOnCollectedDelegate MDA,则错误将成为空引用异常,因此MDA不会出错.我想我必须完全不了解FMOD在这里做了什么.

任何C#+ FMOD大师都能看到我的错误吗?

Han*_*ant 30

    channel.setCallback(channelCallback);
Run Code Online (Sandbox Code Playgroud)

这是问题陈述.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);
    }
Run Code Online (Sandbox Code Playgroud)

你需要从FMod包装器代码中找到实际的委托类型,我只是猜到了"SomeDelegateType".

  • 委托本身就是一个对象.这有时会被C#2.0支持的快捷语法隐藏,编译器代表您进行转换.在你的例子中,代码`channel.setCallback(channelCallback);`由编译器转换为`channel.setCallback(new SomeDelegateType(channelCallback));`. (4认同)
  • 顺便说一句,我对这个问题表示赞赏,你很容易帮助你。问题的完美描述,发布*完全*正确的代码。+1。 (2认同)