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

0

I am having an issue with my web application regularly crashing and resetting the application pool in IIS causing big performance issues, as well as wiping any timing threads running in my application.

The site is a .NET 4.5.2 C# MVC5 Site running on a 2012 Windows Server EC2 instance in AWS.

The issue was first noticed when I started see the site struggle to load after so many minutes of run-time. I thought it might be the ApplicationPool recycling and made sure to set IdleTime and Application Preload in IIS properly. The issue still persisted.

Next I went to the Server Manager to check the event logs and found these entries happening about every 15 minutes or so:

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:

and

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.

Next I ran a DebugDiag2 Collection and Analysis:

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

This is the only error reported by this debugger. With no others exceptions in the .NET stack trace on the report. I can't seem to get the debugging symbols for this particular .dll and the messages do not seem to be very helpful.

The application utilizes WMPLib to create a singleton instance at startup of the wmplayer to play sounds on the Windows Server 2012 instance via web requests from clients. The application works in this regard with no issue playing sounds and requests from multiple users.

Here is the Singleton:

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");
        }
    }
}

and initially instantiated in Startup.cs via:

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

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

I can run this application on my local dev machine with no issues or crashes. Everything functions as expected with no crashing. On deployment to the EC2 instance, everything on the site works properly, but there is now a crash / reset every 15 minutes.

My suspicion is either:

A) It is a problem with the WMPLib instance and some missing dependency on the Windows Server 2012 box that allows it to play sounds but causes crashes on regular intervals.

B) I've made a mistake with my singleton instantiation and it is somehow crashing my application.

I have tried the solution here but no results.

Any help would be appreciated.

edit: I have confirmed the issue is related to the usage of WMPLib as removing its use stopped the crashing every 15 minutes. Still not sure why this happens.

c#
.net
multithreading
iis
wmplib
asked on Stack Overflow Sep 20, 2017 by BagoDev • edited Sep 20, 2017 by BagoDev

2 Answers

2

This is not a direct answer to your question but rather a different way do to the same thing. Instead of the WMPLib COM control, try using the thread-safe MediaPlayer class from WPF. Add references to WindowsBase and PresentationCore, and use something like this instead:

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();
}

You can also use it as a singleton the same way as above, and it is fully thread-safe, which WMPLib is not.

Documentation here.

Edit:

As noted in the comments, you really could use just a static class with a public bool property to show busy signal. A static class in IIS is shared among all requests for an application and the class is only subject to garbage collection when the application pool is recycled, so you do need to be careful with the lifetime of the objects you store in it, to avoid memory consumption problems. This code will use a new instance of the media player class for each PlaySound() and disposes of it right after it's done playing, but the busy flag is common among all requests made to the server.

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");
            }
        }
    }
}
answered on Stack Overflow Sep 20, 2017 by Drunken Code Monkey • edited Sep 21, 2017 by Drunken Code Monkey
1

I ended up using NAudio with my singleton pattern.

As per recommendations from Lex Li, I used a third party as the Windows.MediaPlayer was not meant for a web app. Based of the solution from Drunken Code Monkey, I used a boolean flag on the singleton to evaluate play states being checked frequently by a separate thread evaluating the PlaybackState.Stopped value on the IWavePlayer object in my singleton. My only concern is performance. I haven't noticed any problems yet, but I'm pretty sure managing Events in a Handler would have been more performant and a lot less code if it's even possible to do from a web app.

Here is the code:

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"]);
            }
        }
    }
}

Hopefully this helps anyone running into the same problems. My site has been up and running for a few hours now with no issues and clients spamming the board. Thanks again everyone who helped.

answered on Stack Overflow Sep 21, 2017 by BagoDev

User contributions licensed under CC BY-SA 3.0