收集回调代表?

时间:2011-09-04 20:50:34

标签: c# delegates garbage-collection fmod

为了C#游戏开发一直在搞乱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毫秒同步点。此时,VC#从FMOD.EventSystem.update()中吐出以下错误:

  

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

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

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

2 个答案:

答案 0 :(得分:27)

    channel.setCallback(channelCallback);

这是问题陈述。 FMod是非托管代码。您正在此处创建委托对象并将其传递给非托管代码。问题是,垃圾收集器无法跟踪本机代码所持有的引用。下一个垃圾收集将找到 no 对该对象的引用并收集它。 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”。

答案 1 :(得分:0)

我在VB.NET和自定义C ++ 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 sub中进行了简单的调用:

vidProc_cb_MouseClick(AddressOf updateXY)

我会收到'CallbackOnCollectedDelegate'错误。不是立即,但在与表单上的其他对象交互后,然后尝试调用回调(在我的情况下是在OpenCV窗口中单击鼠标)。

修正:

1)声明(在表格类,声明中)

Private addr_update As CB_FUNC

2)在Form Load

上定义addr_update
addr_update = New CB_FUNC(AddressOf updateXY)

3)使用新指针调用我的'set callback'函数(在button_click sub中)

vidProc_cb_MouseClick(addr_update)

我想我理解@Hans并正确实现它(我无法重现错误)。希望这有助于某人。