Search code examples
c#winformsexceptioncustom-exceptionsthread-exceptions

Application.ThreadException is getting incorrect Win32Exception type


We have a very large complicated application that has a ThreadException handler intialized at startup, and any exceptions thrown by the application that are not handled immediately are handled by this ThreadException handler in a unified way.

This mostly works...but we have a couple custom exception types that we want to handle with this exception handler, and for some reason these exception types always show up in the ThreadException handler as just System.ComponentModel.Win32Exception types, instead of our custom types.

I've tried anything I can think of to troubleshoot, including making sure our custom exception classes implement all recommended constructors, including a serialiation constructor.

Additional info...when I create a new exception with just the message from the existing exception, this comes through as a System.Exception. For example:

                MSCSqlException msx = new MSCSqlException(sqlQuery, sqlParams, sqlException);
                throw new Exception(ex.Message);

works fine and is caught in the exception handler as a System.Exception.

However, if I try something like:

                MSCSqlException msx = new MSCSqlException(sqlQuery, sqlParams, sqlException);
                throw new Exception(ex.Message, ex);

Then the exception manager catches the above System.ComponentModel.Win32Exception instead of just a System.Exception.

Just for completeness, what I WANT to do is something like:

                throw new MSCSqlException(sqlQuery, sqlParams, sqlException);

and have the Application.ThreadException handler receive a properly typed MSCSqlException.

Any ideas how to get around this? Is there some quirk of Application.ThreadException that I'm missing related to custom error types?

Our custom exception class:

[Serializable]
public class MSCSqlException : Exception
{
    public string SqlCommand { get; private set; }
    public object[] SqlParameters { get; private set; }

    public MSCSqlException()
    {
    }

    public MSCSqlException(string message)
        : base(message)
    {
    }

    public MSCSqlException(string message, Exception inner) : base(message, inner)
    {
    }

    public MSCSqlException(string command, object[] parameters, SqlException sx) : base(CreateUsefulMessage(sx, command, parameters), sx)
    {
        SqlCommand = command;
        SqlParameters = parameters;
    }

    protected MSCSqlException(SerializationInfo info, StreamingContext context) : base(info, context)
    {
        SqlCommand = info.GetString("SqlCommand");
    }

    [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (info == null)
        {
            throw new ArgumentNullException("info");
        }

        info.AddValue("SqlCommand", SqlCommand);
        base.GetObjectData(info, context);
    }

    public static string CreateUsefulMessage(SqlException sx, string sqlCommand, object[] sqlParameters)
    {
        string message = sx.Message + Environment.NewLine;

        if(sqlParameters != null && sqlParameters.Count() > 0)
        {
            message = message + "Parameters:" + Environment.NewLine;
            foreach(object sp in sqlParameters)
            {
                message = message + "\t" + sp.ToString() + Environment.NewLine;
            }
        }

        message = message + "SQL Statement:" + Environment.NewLine;
        message = message + sqlCommand;

        return message;
    }
}

Solution

  • We finally figured this out...the issue is a bug or feature in how .Net handles exceptions thrown in a background worker Completed event. For whatever reason, it appears that in this case, the .Net run time is passing the innermost exception from the stack trace to the ThreadException handler instead of the expected outermost exception. This innermost exception when a SQLException is thrown is a Win32Exception.

    More info on this here:

    Application.ThreadException is getting incorrect Win32Exception type