0

In my library, I use a SynchronizationContext to enable me easily to raise events on the GUI thread whether the library is being used in a Windows Forms or WPF app. If my class was created on a background thread, the SynchronizationContext is null, so I raise the event directly.

For example:

    private void RaisePlaybackStoppedEvent()
    {
        EventHandler handler = PlaybackStopped;
        if (handler != null)
        {
            if (this.syncContext == null)
            {
                handler(this, EventArgs.Empty);
            }
            else
            {
                this.syncContext.Post(state => handler(this, EventArgs.Empty), null);
            }
        }
    }

This works fine, except that I have had user reports that it goes wrong in ASP.NET applications:

System.NullReferenceException was unhandled
  Message=Object reference not set to an instance of an object.
  Source=System.Web
  StackTrace:
       at System.Web.HttpApplication.ThreadContext.Enter(Boolean setImpersonationContext)
       at System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext)
       at System.Web.AspNetSynchronizationContext.CallCallbackPossiblyUnderLock(SendOrPostCallback callback, Object state)
       at System.Web.AspNetSynchronizationContext.CallCallback(SendOrPostCallback callback, Object state)
       at System.Web.AspNetSynchronizationContext.Post(SendOrPostCallback callback, Object state)

I had assumed that SynchronizationContext would be null in an ASP.NET website. I need a way to not use the SynchronizationContext if it is an instance of AspNetSynchronizationContext, although to do this would be rather hacky as I have no reference to System.Web and it isn't a publicly visible type anyway.

My question is, what is the best way to determine whether I am in an application with a GUI (e.g. WinForms, WPF, WinRT), and only use SynchronizationContext in that setting?

Mark Heath
  • 48,273
  • 29
  • 137
  • 194

1 Answers1

1

This looks like a bug in ASP.NET: if the AspNetSynchronizationContext is used after the corresponding request has completed, the HttpApplication has cleared its _context field. It then passes this null reference to the ThreadContext constructor, and the methods on the ThreadContext class attempt to access members of the field, causing the NullReferenceException.

I know they made some changes to this area in 4.5, so it might already be fixed. If not, it's probably worth reporting on Connect.

As a workaround, you could try examining the type name of the current SynchronizationContext:

var context = SynchronizationContext.Current;
if (null != context && "AspNetSynchronizationContext" == context.GetType().Name)
{
   context = null;
}

this.syncContext = context;
Richard Deeming
  • 29,830
  • 10
  • 79
  • 151
  • thanks, this is pointing me along the right lines. The request is almost certain to have completed, as my library is for audio playback, so the event could be fired minutes after the playback was kicked off (not really sure why people want to play audio on their webserver, but that's what some of my users are doing). I guess I'll have to use your workaround unless there is a better way to know if I'm in an ASP.NET app. – Mark Heath Oct 24 '12 at 13:56
  • 1
    It definitely sounds like your users are doing something wrong - unless they're trying to spook your sysadmin, who's going to hear the sound playing? Does the server even have a sound card? They probably want the sound to play on the *client*, and don't understand the difference. You might want to check `Environment.UserInteractive`, and either throw an exception or just do nothing if it returns `false`. – Richard Deeming Oct 24 '12 at 14:24
  • Yes, it is a strange request, but I do get people from time to time trying to do this. `Environment.UserInteractive` looks like it could be useful to me as well. – Mark Heath Oct 24 '12 at 17:33