是什么导致Winforms静默地丢弃未处理的异常(没有try/ catch)

本文关键字:没有 异常 try catch 未处理 Winforms 静默 是什么 | 更新日期: 2023-09-27 18:03:27

我正在为我的winforms控件添加新功能,其中一部分要求曾经总是使用的变量现在是可选的(如果它是空的,从第二个来源获取数据)。我做了一些改变并运行了我的表单,却发现什么都没有发生,甚至以前工作的功能。困惑的是,我通过代码一步,发现我的Winforms用户控件抛出了一个NullReferenceException,当它遇到我的变量,但在UI没有错误被抛出。

我的设置是我有一个带有组合框的UserControl。当用户更改组合框时,它会在第一个控件的面板中加载第二个UserControl。第二个控制是抛出异常的原因。

代码路径如下:

    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 == cmbCaseType.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);
        }
    }

NullReferenceException来自SetupDataGrid()方法,_formInstance正在被调用。但是,通常当应用程序遇到未处理的异常时,JIT系统会抛出这样的错误消息(正如您所看到的,除非我是盲人,否则没有使用try/catch语句)。

为什么我的winforms应用程序没有显示异常发生的迹象。我宁愿出现一个未处理的异常消息,也不愿什么都不发生,因为这会让用户更难知道出了什么严重的问题(而不是不响应他们的命令)


编辑:为了澄清,因为似乎有一些混乱,我不关心在调试时在visual studio中打破这个异常。问题的事实是,应用程序不应该隐藏未处理的异常,我的应用程序应该崩溃(或者更确切地说,显示发生了未处理的异常的JIT消息),即使不在visual studio之外的调试模式下也是如此。

这不是调试时的问题,而是生产运行时的问题。例如,如果这段代码抛出一个OutOfMemoryException,我需要它不被静默地忽略。现在它被忽略了

是什么导致Winforms静默地丢弃未处理的异常(没有try/ catch)

从Visual Studio的菜单栏中选择Debug->Exceptions...(如果使用默认快捷方式,则选择Ctrl-D, E),选中Common Language Runtime Exceptions下的Thrown。这将导致程序中断异常,即使它们位于try/catch块中。然后,您可以向前执行一步,查看代码跳转到下一条指令的位置。这会把你带到隐藏异常的catch块。

它可能会跳转到。net代码中进行捕获,您可以转到Debug->Options and Settings..并打开Enable .NET framework Source Stepping查看它跳转到什么。


下面是一个捕捉代码 中未处理异常的示例
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();
    }
}

点击按钮,出现1,之后程序仍应运行。

然而,正如我在上一个评论中所说的,检查user- unhandled被检查(可能取消和重新检查)在Debug->Exceptions首先它可能会解决你的初始问题。

如果您在Main中有try/catch,或者如果您有ThreadException处理程序,则可以捕获它们。也可以查看SetUnhandledExceptionMode .

注意,如果它们在Main中被捕获,您的程序将静静地退出。ThreadException可以"捕捉"并忽略它们。

EDIT:因为当你取消Common Language Runtime ExceptionsThrown的复选框时,调试器不会中断,它被Windows窗体的代码捕获。这并不理想,但是很多BCL都是这样做的。你不需要担心它会捕获OutOfMemoryException或类似的东西;BCL应该只捕捉它期望的异常(恼人的异常)。

需要说明的是,该异常不是"未处理的"。它由BCL代码处理。