当 XNA windows 游戏关闭时,如何阻止 SoundEffect 使它们崩溃?

Posted

技术标签:

【中文标题】当 XNA windows 游戏关闭时,如何阻止 SoundEffect 使它们崩溃?【英文标题】:How do you stop SoundEffect from crashing XNA windows games when they are closed? 【发布时间】:2011-02-24 17:45:08 【问题描述】:

我正在使用 Visual C# Express 在 xna 中为 windows 创建一个游戏。在游戏中,有六个 SoundEffect 对象定期调用其 Play() 方法。问题是有时当游戏关闭时它会崩溃。 似乎在播放音效时关闭窗口时会发生这种情况。这是在 Visual C# 中弹出的消息:

AccessViolationException 未处理

试图读取或写入受保护的内存。这通常表明其他内存已损坏。

Visual Studio 中没有任何可调试的源,当单击“获取此异常的一般帮助”时,会弹出一个空白页面..

使用的代码看起来很像 MSDN 示例。这看起来像是存在于底层框架某处某处的问题,而不是我的代码。但我当然不确定。这已经发生了很多次。

http://msdn.microsoft.com/en-us/library/bb195053.aspx

以下是完整的异常详情:

System.AccessViolationException was unhandled
  Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
  Source=Microsoft.Xna.Framework
  StackTrace:
       at Microsoft.Xna.Framework.Audio.AudioCallbackDispatcher.IsEventRegistered(EventType type)
       at Microsoft.Xna.Framework.Audio.AudioCallbackDispatcher.UnregisterEvent(EventType type)
       at Microsoft.Xna.Framework.Audio.KernelMicrophone.ShutdownCaptureEngine()
       at Microsoft.Xna.Framework.Audio.MicrophoneUnsafeNativeMethods.ShutdownCaptureEngine()
       at Microsoft.Xna.Framework.Audio.AudioRendererShutdownHandler.AppExitingEventHandler(Object sender, EventArgs args)
  InnerException:

(我也有通过 MediaPlayer 播放的音乐,但我认为这无关。)

编辑:我似乎找到了一些可行的方法,但它有点老套,真的没有必要。我仍然愿意接受任何更优雅的解决方案。

在 Game1.UnloadContent() 中调用这一行。它将确保(如果您的音效都短于 3 秒)在程序实际关闭时没有声音播放。

System.Threading.Thread.Sleep(3000);

【问题讨论】:

UnloadContent 的代码是什么样的? (而且,从模板项目开始时,你能重现这个吗?) 【参考方案1】:

将 SoundEffect 对象设为类成员,并在类解构时调用 SoundEffect 的 Dispose() 方法:

class MyClass

    ~MyClass()
    
        effect.Dispose();
    

    SoundEffect effect;


这应该让 SoundEffect 对象在您关闭游戏时自行清理。您可以在此处阅读有关一次性对象的信息:http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

【讨论】:

我确实这样做了,但崩溃仍然发生。 您是否尝试过从 SoundEffect 对象获取 SoundEffectInstance 并从 SoundEffectInstance 调用 Play? 这里使用析构函数和Dispose 非常不正确!。您不应在终结器中的另一个对象上调用Dispose,因为该对象可能在终结器运行时已经终结。终结器永远不应该引用另一个对象。 (正确的说法是破坏,而不是破坏)。 你的第一个答案也是不正确的——但没有那么严重。 否决,因为在 C# 中不能保证在进程退出时调用终结器,因此上面的代码将没有预期的效果。【参考方案2】:

你能让 MyClass 实现 IDisposable 并在该方法中处理 SoundEffect 吗?

【讨论】:

ContentManager.Load 加载的资源归 ContentManager 本身“所有”,应使用ContentManager.Unload 卸载。不要给他们打电话Dispose【参考方案3】:

我认为可以肯定地说这是框架中的一个错误。因为它在音频代码中,所以框架可能没有正确处理从驱动程序获取的内容。很难确定。

可以说从框架中出来的AccessViolationException 是不“正常的”。这几乎可以肯定不是你的错

发生异常的函数IsEventRegisteredunsafe函数。因此,该函数很可能正在执行异常所说的操作:它正在访问无效的内存地址。

异常来自音频捕获(麦克风)的关闭代码,那么您是否在代码中对麦克风进行了处理?您可以尝试使用/不使用麦克风,看看会发生什么。

另外:在没有附加调试器的情况下运行时会发生这种情况吗? (Ctrl+F5)

至于解决问题:您的解决方案不错。

如果你等不起这三秒钟,并且你想弄脏自己的手并编写一些非常有问题(半不可移植,不一定向前兼容)代码:你可以使用反射来访问音频系统的私有属性。查找每当您调用 SoundEffect.Play 时在内部创建的 SoundEffectInstance 对象列表,然后在关闭之前停止这些实例。

或者您也可以通过从不调用Play 而是调用CreateInstance 并自行管理即发即弃音效来有效地做同样的事情。缺点是这需要编写大量代码!

【讨论】:

【参考方案4】:

我遇到了同样的问题,我在类终结器(析构函数)中为我的声音集合做了空值。对我有用。

public class Audio

    private ContentManager content;
    public List<SoundEffectInstance> SoundInstance  get; private set; 
    public AudioEmitter Emitter  get; set; 
    public AudioListener Listener  get; set; 
    public List<SoundEffect> Sound  get; set; 
    public Audio(ContentManager content)
    
        this.content = content;
        Emitter = new AudioEmitter();
        Listener = new AudioListener();
        Sound = new List<SoundEffect>();
        SoundInstance = new List<SoundEffectInstance>();
    
    //set to null your sound instances and effects :D
    ~Audio()
    
        Sound = null;
        SoundInstance = null;
    
...

【讨论】:

以上是关于当 XNA windows 游戏关闭时,如何阻止 SoundEffect 使它们崩溃?的主要内容,如果未能解决你的问题,请参考以下文章

音乐重复时 WP7 XNA 游戏“断断续续”

在运行 XNA 游戏时在 Visual Studio 中观看控制台输出面板

当另一个声音使用 XNA/XACT 结束时,如何立即播放一个声音?

FBX 模型在 XNA 4.0 中无法正确显示

如何让Win10在重启关机或注销时自动关闭应用程序

如何阻止 XAMPP 干扰 Windows 中的 ISS