.NET WinForms应用程序在退出时因未处理的System.NullReferenceException而崩溃

时间:2012-04-26 15:33:53

标签: c# .net winforms backgroundworker unhandled-exception

我有一个WinForms应用程序,其中包含一个带有后台工作程序的表单。该表单包含一个按钮,该按钮通过RunWorkerAsync()启动后台工作程序,另一个按钮将退出应用程序。大约1/3的时间,在后台工作者完成其工作后,应用程序将在我单击“退出”按钮后崩溃,例外情况如下:

System.NullReferenceException was unhandled
  Message=Object reference not set to an instance of an object.
  Source=System.Drawing
  StackTrace:
       at System.Drawing.Graphics.Dispose(Boolean disposing)
       at System.Drawing.Graphics.Finalize()

以下是退出应用程序的按钮的事件处理程序:

    private void buttonExit_Click(object sender, EventArgs e)
    {
        if (!buttonStartWorker.Enabled)
        {
            DialogResult dr = MessageBox.Show("Background worker is still running!  Exit anyway?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);

            if (dr == DialogResult.OK)
            {
                backgroundWorker.CancelAsync();
                Close();
            }
        }
        else
        {
            Close();
        }
    }

正如我之前所说的,当后台工作程序仍在运行时,我没有退出应用程序,所以我们在这里查看的代码路径只是Close()调用。还有一个FormClosing事件处理程序,它在我的USB相关句柄上调用close和dispose方法。该代码如下:

    private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
    {
        try
        {
            // close and dispose all open handles to the USB device
            if (hidHandle != null)
            {
                if (!(hidHandle.IsInvalid))
                {
                    hidHandle.Close();
                    hidHandle.Dispose();
                }
            }

            if (readHandle != null)
            {
                if (!(readHandle.IsInvalid))
                {
                    readHandle.Close();
                    readHandle.Dispose();
                }
            }

            if (writeHandle != null)
            {
                if (!(writeHandle.IsInvalid))
                {
                    writeHandle.Close();
                    writeHandle.Dispose();  // unhandled exception seems to occur after this
                }
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.ToString());
        }
    }

writeHandle.Dispose()和应用程序实际退出的时间之间的某个时间,此异常正在发生。让我最困惑的事情是我的代码从未明确地使用System.Drawing,所以我无法跟踪它。

对于它的价值,我的后台工作人员会做以下事情:

  1. 它向USB设备读取和写入一些数据
  2. 它创建了一个Web客户端来下载一些数据
  3. 进行一些SOAP调用
  4. 当应用程序(未明确使用System.Drawing)退出时,是否有人对System.Drawing中可能导致未处理的NullReferenceException的内容有任何想法?

2 个答案:

答案 0 :(得分:1)

尽管KeithS的答案原则上是正确的,但您的代码似乎确实存在明显的问题。您在调用CancelAsync()后立即调用this.Close();。我会尝试等待后台工作者完成其业务或订阅一个已取消的事件(如果存在)。

您的后台工作人员很可能尚未完成。另请注意,后台工作程序是与Form的事件层次结合的组件。

尝试创建新任务:

this.BeginInvoke(new Action(() => (Thread.Sleep(1000); this.Close();)));

抱歉语法不正确,但我不在开发机器上。

答案 1 :(得分:0)

虽然您的程序可能没有显式使用System.Drawing,但该命名空间遍布大多数WinForms对象代码。看来,在您的表单中的某个地方,一个GUI对象被赋予对另一个对象的Graphics句柄的引用,然后它认为它必须销毁;但是,拥有该句柄的控件已经被释放,或者Graphics对象本身已经显式调用了它的Dispose方法,因此Dispose()代码在第二次运行时失败。我原以为.NET开发人员会进行检查以确保双重Dispose不会成为问题,但在这种情况下它们会忽略它。

如果不确切知道你的Windows窗体上有什么,以及System.Drawing.Graphics的哪个实例正在抛出,我真的无法进一步帮助。那将是我进一步调查的地方;尝试发现正在处理的确切实例,其他对象拥有它的原因,以及为什么它在处理完毕后最终确定(通常如果明确处理,则应调用GC.SuppressFinalize())。

其中一个“反映”的代码库引用可能会帮助您;查找System.Drawing.Graphics.Dispose(bool disposing),并浏览连续运行两次会失败的任何代码行。这可能会给你一个提示。