Scott Hanselman

Preventing Dialogs on the Server-Side in ASP.NET or Trace.Fail considered Harmful

September 19, '06 Comments [4] Posted in ASP.NET
Sponsored By

Dialogs on the server-side? What is this nonsense of which you speak? Are you insane? Sadly no.

Back in the day, if you ran a VB6 COM DLL under Classic ASP, or as I like to call it "Poopy-SP," and you forgot to set "Unattended Execution" and "Retain in Memory" you could get yourself into a pickle if an error in the application caused a dialogbox or messagebox to pop up on the Server-side, just waiting for some poor schmuck to click "OK."

Recently I got an email that a high-powered blogger was seeing Dialog Boxes on his hosted instance of DasBlog while running under ASP.NET 2.0. These dialogs would hang the thread they were on, and hang IIS until they were dismissed. Shocked he and I were to say the least as "that ain't supposed to happen."

I suggested that he disable JIT Debugging in his registry. He tried it and indicated that he was getting a full stack trace inside the message box. Since the JIT Debugging Dialog doesn't look like that, I figured that wasn't the problem.

I spoke to some folks at MSFT who said that if there were unhandled exceptions being thrown from DasBlog, how they'd be presented would be controlled via the legacyUnhandledExceptionPolicy setting in his web.config like this (we don't have any unhandled exceptions, BTW, there's 'global' exception handlers in two places):

<configuration>
  <runtime>
    <legacyUnhandledExceptionPolicy enabled="true" />
  </runtime>
</configuration>

This tells the CLR to act more like .NET 1.1 in this respect as the behavior for unhandled exceptions has changed in 2.0 (MSDN Mag Article here and MSDN Topic here).

This "solution" smelled fishy to me, didn't seem like a good idea, and ultimately didn't solve his problem.

He then indicated that he was seeing errors in our SharedBasePageErrorHandler, which made sense to me as we handle all Page-level exceptions there.

private void SharedBasePageErrorHandler(object sender, EventArgs e )
{
    Exception pageException = Server.GetLastError();
    try
    {
        loggingService.AddEvent(
            new EventDataItem(EventCodes.Error,
            pageException.ToString().Replace("\n","<br>"),
            this.Page.Request.Url.ToString()));
        System.Diagnostics.Trace.Fail(this.Context.Error.Message);
    }
    catch
    {
    }
}

Seems pretty straightforward, and his stuff WAS getting logged indicating that the LoggingService.AddEvent was working fine.

What about this System.Diagnostics.Trace.Fail call, though? Seems harmless, but it's the only other thing in there and it would explain where the stack trace was coming from - it's inside of this.Context.Error.Message.

There was a problem back in the day with Trace.Fail or Debug.Fail locking up the ASP.NET Worker Process.

If you reflector into the implementation of the DefaultTraceListener in the .NET Framework you'll see a call to AssertWrapper.ShowAssert (that should only happen if AssertUIEnabled is true and UI Permissions are available.) That must be the case in our blogger friend's situation.

public override void Fail(string message, string detailMessage)
{
      string text1;
      StackTrace trace1 = new StackTrace(true);
      int num1 = 0;
      bool flag1 = DefaultTraceListener.UiPermission;
      try
      {
            text1 = this.StackTraceToString(trace1, num1, trace1.FrameCount - 1);
      }
      catch
      {
            text1 = "";
      }
      this.WriteAssert(text1, message, detailMessage);
      if (this.AssertUiEnabled && flag1)
      {
            AssertWrapper.ShowAssert(text1, trace1.GetFrame(num1), message, detailMessage);
      }
}

Assuming those things are true and ShowAssert does get called, interally the AssertWrapper.ShowAssert calls SafeNativeMethods.MessageBox only if the user is in an interactive mode. It's possible that MB_SERVICE_NOTIFICATION isn't being passed into the MessageBox indicating that it's a non-interactive service who is throwing the box.

Setting this option in the web.config (or machine.config) works around the issue by not even getting into AssertWrapper.

<configuration>
   <system.diagnostics>
      <assert assertuienabled="false" logfilename="c:\log.txt"/>
   </system.diagnostics>
</configuration>

This looks like a bug in ASP.NET 2.0, but we'll see.

The workarounds are:

  • Don't use Trace.Fail()

or

  • Set AssertUIEnabled="false" in your web.config.

The strange part about this is why it's seen on some systems and not others.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb
Tuesday, 19 September 2006 01:01:30 UTC
This is why I use log4net. ;)

Out of curiosity, would a third workaround be to remove the DefaultTraceListener? According to the MSDN docs on Trace.Fail:
If the DefaultTraceListener is not in the Listeners collection, the message box is not displayed.
Tuesday, 19 September 2006 04:43:37 UTC
This problem is one of the main things that drove me off dasBlog and onto Wordpress several months ago. I didn't run my own server (I used the hosting service provided by DiscountASP.NET) and I kept having this problem that my worker process would hang. They told me there were dialog boxes up on the server. I think I even had some mail or comments with you, Scott, where you said that shouldn't be possible. Since I didn't have anything besides FTP access to the server, I couldn't diagnose it.

I'm glad to know there's a solution to the problem.
Tuesday, 19 September 2006 07:04:36 UTC
Well, it's nice to know that it wasn't DasBlog. The ASP.NET Team was a little shocked themselves.
Wednesday, 20 September 2006 12:35:12 UTC
I had the same problem with asserts on DasBlog and setting assertuienabled="false" seemd to help. I just forgot to report it to DasBlog team since it didn't bother me anymore :-)
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.