WMNetMgr.dll 应用程序错误 w3wp.exe 使用 WMPLib C# .NET 时出错

WMNetMgr.dll Application Error w3wp.exe error using WMPLib C# .NET

我的 Web 应用程序经常崩溃并重置 IIS 中的应用程序池,导致严重的性能问题,并且擦除我的应用程序中的所有计时线程 运行ning。

该站点是 .NET 4.5.2 C# MVC5 站点 运行 宁在 2012 Windows AWS 中的服务器 EC2 实例上。

这个问题是在我开始看到网站在 运行 几分钟后难以加载时第一次注意到的。我认为这可能是 ApplicationPool 回收,并确保在 IIS 中正确设置 IdleTime 和 Application Preload。问题仍然存在。

接下来我去服务器管理器检查事件日志,发现这些条目大约每 15 分钟发生一次:

Faulting application name: w3wp.exe, version: 8.5.9600.16384, time stamp: 0x5215df96 Faulting module name: WMNetMgr.dll_unloaded, version: 12.0.9600.17415, time stamp: 0x545047db Exception code: 0xc0000005 Fault offset: 0x00000000000cf5cf Faulting process id: 0x17d0 Faulting application start time: 0x01d331dc20f096d0 Faulting application path: c:\windows\system32\inetsrv\w3wp.exe Faulting module path: WMNetMgr.dll Report Id: 777a35de-9dd1-11e7-81d7-025ff0be916d Faulting package full name: Faulting package-relative application ID:

WIN-7PCRJOFR05F 5011 Warning Microsoft-Windows-WAS System 9/20/2017 7:01:04 AM - A process serving application pool 'SiteName' suffered a fatal communication error with the Windows Process Activation Service. The process id was '6096'. The data field contains the error number.

接下来我运行一个DebugDiag2的收集与分析:

WARNING - DebugDiag was not able to locate debug symbols for WMNetMgr.dll>, so the information below may be incomplete. In w3wp__SiteName__PID__5088__Date__09_20_2017__Time_06_31_02AM__436__Second_Chance_Exception_C0000005.dmp an access violation exception (0xC0000005) occured on thread 26 when another Module attempted to call the following unloaded Module: WMNetMgr.dll>.

Thread 26: Call Stack Unloaded_WMNetMgr.dll+cf5cf 0x000000de575cf7c0 0x000000dc2ed5ec10

这是此调试器报告的唯一错误。在报告的 .NET 堆栈跟踪中没有其他异常。我似乎无法获得此特定 .dll 的调试符号,而且消息似乎也不是很有帮助。

该应用程序利用 WMPLib 在 wmplayer 启动时创建一个单例实例,以通过来自客户端的 Web 请求在 Windows Server 2012 实例上播放声音。该应用程序在这方面可以正常播放声音和来自多个用户的请求。

这是单例:

public sealed class SoundboardSingleton : IDisposable
{
    private static readonly Lazy<SoundboardSingleton> lazy =
        new Lazy<SoundboardSingleton>(() => new SoundboardSingleton());

    public static SoundboardSingleton Instance { get { return lazy.Value; } }

    public WindowsMediaPlayer WMPlayer;
    private StayAliveBot _liveBot;
    private Thread _botThread;

    private SoundboardSingleton()
    {
        WMPlayer = new WindowsMediaPlayer();
        WMPlayer.settings.volume = 50;

        _liveBot = new StayAliveBot();
        _botThread = new Thread(_liveBot.Live);
        _botThread.Start();
    }

    public void Dispose()
    {
        if (_botThread.IsAlive)
        {
            _botThread.Abort();
        }
    }
}

public class StayAliveBot
{
    public void Live()
    {
        while (SoundboardSingleton.Instance != null)
        {
            Thread.Sleep(1500000);
            SoundboardHelper.PlaySound("C:\SoundboardOpFiles\TestTone.wav");
        }
    }
}

并最初在 Startup.cs 中通过以下方式实例化:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);

        // startup soundboard singleton
        SoundboardSingleton.Instance.WMPlayer.settings.volume = 50;
    }
}

我可以在我的本地开发机器上 运行 这个应用程序,没有任何问题或崩溃。一切都按预期运行,没有崩溃。在部署到 EC2 实例时,网站上的一切正常,但现在每 15 分钟就会发生一次崩溃/重置。

我的怀疑是:

A) WMPLib 实例存在问题,缺少对 Windows Server 2012 框的依赖,允许它播放声音但会定期导致崩溃。

B) 我在单例实例化时犯了一个错误,它以某种方式使我的应用程序崩溃。

我已经尝试了 here 的解决方案,但没有结果。

如有任何帮助,我们将不胜感激。

编辑:我已经确认问题与 WMPLib 的使用有关,因为删除它的使用停止了每 15 分钟一次的崩溃。仍然不确定为什么会这样。

这不是对您问题的直接回答,而是对同一件事的不同处理方式。尝试使用 WPF 中的线程安全 MediaPlayer class 而不是 WMPLib COM 控件。添加对 WindowsBase 和 PresentationCore 的引用,并改用如下内容:

using System.Windows.Media;

public void PlaySound(string filename)
{
    var mplayer = new MediaPlayer();
    mplayer.MediaEnded += new EventHandler(MediaEndedHandler);
    mplayer.Open(new Uri(filename));
    mplayer.Play();
}

public void MediaEndedHandler(object sender, EventArgs e)
{
    ((MediaPlayer)sender).Close();
}

您也可以像上面一样将它用作单例,并且它是完全线程安全的,而 WMPLib 则不是。

Documentation here

编辑:

如评论中所述,您真的可以只使用带有 public 布尔值 属性 的静态 class 来显示忙音。 IIS 中的静态 class 在应用程序的所有请求之间共享,并且 class 仅在回收应用程序池时受到垃圾收集,因此您需要注意对象的生命周期您存储在其中,以避免内存消耗问题。此代码将为每个 PlaySound() 使用媒体播放器 class 的新实例,并在播放结束后立即处理它,但忙标志在对服务器发出的所有请求中很常见。

using System;
using System.Threading;
using System.Windows.Media;

namespace SoundBoardApp
{
    public static class Soundboard
    {
        private static bool _isBusy = false;

        public static bool IsBusy { get { return _isBusy; } }

        private static void MediaEndedHandler(object sender, EventArgs e)
        {
            _isBusy = false;
            var wmp = ((MediaPlayer)sender);
            wmp.MediaEnded -= new EventHandler(MediaEndedHandler);
            wmp.Close();            
        }

        public static bool PlaySound(string filename)
        {
            if (!_isBusy)
            {
                _isBusy = true;
                var wmp = new MediaPlayer();
                wmp.MediaEnded += new EventHandler(MediaEndedHandler);
                wmp.Volume = 0.5;
                wmp.Open(new Uri(filename));
                wmp.Play();
                return true;
            }
            else
            {
                return false;
            }            
        }

    }

    public class StayAliveBot
    {
        public void Live()
        {
            while (true)
            {
                Thread.Sleep(1500000);
                if (!Soundboard.IsBusy) Soundboard.PlaySound("C:\SoundboardOpFiles\TestTone.wav");
            }
        }
    }
}

我最终使用 NAudio 和我的单例模式。

根据 Lex Li 的建议,我使用了第三方,因为 Windows.MediaPlayer 不适用于网络应用程序。基于 Drunken Code Monkey 的解决方案,我在单例上使用了一个布尔标志来评估播放状态,该状态由一个单独的线程经常检查,该线程评估单例中 IWavePlayer 对象上的 PlaybackState.Stopped 值。我唯一关心的是性能。我还没有注意到任何问题,但我敢肯定,如果甚至可以从 Web 应用程序来管理事件,那么在处理程序中管理事件的性能会更高,代码也会更少。

代码如下:

using NAudio.Wave;

public sealed class SoundboardSingleton : IDisposable
{
    private static readonly Lazy<SoundboardSingleton> lazy =
        new Lazy<SoundboardSingleton>(() => new SoundboardSingleton());

    public static SoundboardSingleton Instance { get { return lazy.Value; } }

    public IWavePlayer WaveOutDevice { get; set; }
    public AudioFileReader AudioFileReaderObj { get; set; }
    public float Volume { get; set; }

    private MediaCloser _mediaCloser;
    private Thread _mediaCloserThread;
    private StayAliveBot _liveBot;
    private Thread _botThread;

    public bool IsBusy { get; set; }

    private SoundboardSingleton()
    {
        // checks our NAudio WaveOutDevice playback for stop
        _mediaCloser = new MediaCloser();
        _mediaCloserThread = new Thread(_mediaCloser.CheckForStoppedPlayback);
        _mediaCloserThread.Start();

        // thread to play sound every 25 minutes, to avoid idle flag
        _liveBot = new StayAliveBot();
        _botThread = new Thread(_liveBot.Live);
        _botThread.Start();
    }

    public bool PlaySound(string filename)
    {
        // make sure we are not active
        if (IsBusy) { return false; }

        // process sound
        IsBusy = true;
        WaveOutDevice = new WaveOutEvent();
        AudioFileReaderObj = new AudioFileReader(filename);
        AudioFileReaderObj.Volume = Volume;
        WaveOutDevice.Init(AudioFileReaderObj);
        WaveOutDevice.Play();

        return true;
    }

    public void CloseWaveOut()
    {
        // clean up sound objects
        WaveOutDevice?.Stop();

        if (AudioFileReaderObj != null)
        {
            AudioFileReaderObj.Dispose();
            AudioFileReaderObj = null;
        }
        if (WaveOutDevice != null)
        {
            WaveOutDevice.Dispose();
            WaveOutDevice = null;
        }
    }

    public void Dispose()
    {
        if (_mediaCloserThread.IsAlive)
        {
            _mediaCloserThread.Abort();
        }
        if (_botThread.IsAlive)
        {
            _botThread.Abort();
        }
    }
}

public class MediaCloser
{
    public void CheckForStoppedPlayback()
    {
        while (true)
        {
            // continuously check for our stopped playback state to cleanup
            Thread.Sleep(500);
            if (SoundboardSingleton.Instance.WaveOutDevice != null &&
                SoundboardSingleton.Instance.WaveOutDevice.PlaybackState == PlaybackState.Stopped)
            {
                SoundboardSingleton.Instance.CloseWaveOut();
                SoundboardSingleton.Instance.IsBusy = false;
            }
        }
    }
}

public class StayAliveBot
{
    public void Live()
    {
        while (true)
        {
            // prevent bot from going idle
            Thread.Sleep(1500000);
            if (!SoundboardSingleton.Instance.IsBusy)
            {
                SoundboardSingleton.Instance.PlaySound(ConfigurationManager.AppSettings["SoundboardHeartbeatFile"]);
            }
        }
    }
}

希望这可以帮助 运行 遇到同样问题的任何人。我的网站已经正常运行 运行 几个小时了,没有任何问题,而且客户向董事会发送垃圾邮件。再次感谢所有帮助过的人。