开发者

What causes Winforms to silently discard unhandled exceptions (with no try/Catches)?

I'm in the middle of adding new functionality to my winforms controls, and part of that requires that a variable that was once always used to now be optional (if it's null, get the data from a second source). I made some changes and ran my form, only to find out nothing was happening, even functionality that previously worked. Confused I stepped through the code and found out that my Winforms user control was throwing a NullReferenceException when it encountered my variable, but in the UI no errors were being thrown.

My setup is I have a UserControl with a combo box. When the user changes that combo box it loads a secondary UserControl in a panel the first control has. The second control is what is throwing the exception.

Here are the code paths:

    private void cmbActionType_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (_loading)
            return;

        // ActionType was changed, update the action.ActionType value
        if (cmbActionType.SelectedItem != null)
        {
            if (cmbActionType.SelectedItem.ToString() == SETVALUE_OPTION)
                _action.ActionType = ActionTypes.SetValue;
            else if (cmbActionType.SelectedItem.ToString() == CHECKVALUE_OPTION)
                _action.ActionType = ActionTypes.CheckValue;
            else
                _action.ActionType = ActionTypes.CustomAction;
        }

        RefreshActionPanel();
        _editor.DataModified();
    } 

    private void RefreshActionPanel()
    {
        // Control defaults
        AnchorStyles styles = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;
        UserControl subControl = null;

        // Clear the currently active control
        pnlActionDetails.Controls.Clear();

        // Determine what type of control to load in the panel
        if (cmbActionType.SelectedItem != null && cmbCaseType.SelectedItem != null)
        {
            // SetValue or CheckValue actions
            if (cmbActionType.SelectedItem.ToString() == CHECKVALUE_OPTION || cmbActionType.SelectedItem.ToString() == SETVALUE_OPTION)
            {
                if (_caseTypeMap.ContainsKey(cmbCaseType.SelectedItem.ToString()))
                    subControl = new SetCheckActionControl(_action, _editor, _caseTypeMap[cmbCaseType.SelectedItem.ToString()]);
            }

            // CustomAction action type
            else
            {
                // Check if the requested case is a type or defined in a script
                if (_caseTypeMap.ContainsKey(cmbCaseType.SelectedItem.ToString()))
                {
                    subControl = new CustomActionControl(_action, _editor, _caseTypeMap[cmbCaseType.SelectedItem.ToString()]);
                }

                else if (_editor.ScriptDefinitions.Any(x => x.CaseName == cmbCaseType.SelectedItem.ToString()))
                {
                    var definitions = _editor.ScriptDefinitions.Where(x => x.CaseName == cmbCa开发者_运维百科seType.SelectedItem.ToString()).ToList();
                    subControl = new CustomActionControl(_action, _editor, definitions);
                }
            }
        }

        if (subControl != null)
        {
            subControl.Anchor = styles;
            subControl.Height = pnlActionDetails.Height;
            subControl.Width = pnlActionDetails.Width;
            pnlActionDetails.Controls.Add(subControl);
        }
    }

    public CustomActionControl(TestAction action, fmEditor editor, IList<TcScriptDefinition> scriptDefinitions) : base(action, editor)
    {
        _loading = true;
        InitializeComponent();

        _scriptDefinitions = scriptDefinitions;

        PopulateActionList();
        SetupDataGrid();

        _loading = false;
    }

    private void SetupDataGrid()
    {
        // Clear the current contents of the datagrid
        grdParameters.Rows.Clear();

        if (cmbAction.SelectedItem == null)
            return;

        // Retrieve the action code from the drop down
        string actionCode = cmbAction.SelectedValue.ToString();

        // Check if any paramters are available for this action
        if (!_availableActionParameters.ContainsKey(actionCode))
            return;

        // Add a new row for each parameter available for this action
        foreach (string param in _availableActionParameters[actionCode])
        {
            string display = param;

            // Determine if the parameter has a display string
            if (_formInstance.CodeDisplayMap.ContainsCode(param))
                display = _formInstance.CodeDisplayMap.GetDisplayStringFromCode(param);

            // Create the array for the row, with an empty string as the current value
            string[] row = { display, string.Empty };

            // Check if the current action uses this action code.  
            //   If so, retrieve the value for this parameter and use it in the row
            //   Note: Case-INsensitive comparison must be performed here
            if (_action.Attributes["action"].Equals(actionCode, StringComparison.CurrentCultureIgnoreCase))
                if (_action.Attributes.ContainsKey(param))
                    row[1] = _action.Attributes[param];

            grdParameters.Rows.Add(row);
        }
    }

The NullReferenceException is coming from the SetupDataGrid() method where _formInstance is being called. However, usually when an application encounters an unhandled exception the JIT system throws an error message saying such (and as you can see, there's no try/catch statements used unless I am blind).

Why does my winforms application show no signs of an exception occurring. I'd rather an unhandled exception message occur rather than nothing happening, as that makes it harder for users to know something critical went wrong (as opposed to it not responding to their commands)


Edit: To clarify since there seems to be some confusion, I do NOT care about breaking on this exception in visual studio when debugging. The fact of the matter is that the application should not be hiding unhandled exceptions, and my application should crash (or rather show the JIT message that an unhandled exception occurred), even when not in debug mode outside of visual studio.

This is not a debug time question but a production run time question. If this code throws an OutOfMemoryException for instance, I need it to not be silently ignored. Right now it is being ignored.


Go to Debug->Exceptions... (Ctrl-D, E if you are using default shortcuts) from your menu bar in Visual Studio and check the box for Thrown under Common Language Runtime Exceptions. This will cause your program to break on exceptions even if they are in a try/catch block. You can then do a step forward and see where the code is jumping to for the next instruction. That should bring you to the catch block that is hiding your exception.

It may be jumping in to .NET code for the catch, you can go to Debug->Options and Settings.. and turn on Enable .NET framework Source Stepping to see what it is jumping in to.


Here is a example of catching the unhandeled execptions in code

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
        Application.Run(new Form1());
        MessageBox.Show("0");
    }

    static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
    {
        MessageBox.Show("1");

    }
}
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {

        throw new ArgumentNullException();
    }
}

Click the button and 1 appears, the program should still run afterwards.

However as I said in my last comment, check that user-unhandeled is checked (maybe uncheck and recheck) in Debug->Exceptions first it may solve your initial issue..


They can be caught if you have a try/catch in your Main, or if you have a ThreadException handler. Also check out SetUnhandledExceptionMode.

Note that if they're caught in Main, your program will just exit silently. ThreadException can "catch" and ignore them.

EDIT: Since the debugger doesn't break when you uncheck the box for Thrown under Common Language Runtime Exceptions, it's being caught by the code for Windows Forms. This is not ideal, but a lot of the BCL does this. You don't need to worry about it catching OutOfMemoryException or anything like that; the BCL should only be catching exceptions that it expects (vexing exceptions).

To be clear, the exception is not "unhandled". It is handled by the BCL code.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜