0

我有一个 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 调用

当应用程序(未明确使用 System.Drawing)退出时,是否有人对可能导致 System.Drawing 中未处理的 NullReferenceException 的原因有任何想法?

4

2 回答 2

1

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

您的后台工作人员很可能尚未完成。另请注意,后台工作人员是与表单的事件层次结构相关的组件。

尝试创建一个新任务:

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

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

于 2012-04-27T03:16:54.143 回答
0

While your program may not use System.Drawing explicitly, that namespace is all over most WinForms object code. It appears that somewhere in your form, one GUI object is given a reference to another object's Graphics handle, which it then thinks it has to destroy; but, the control that owns that handle has already been disposed, or the Graphics object itself has had its Dispose method called explicitly, and so the Dispose() code fails when run the second time. I would have expected the .NET developers to put in checks to make sure a double-Dispose wouldn't be a problem, but it appears in this case they overlooked it.

Without knowing exactly what you have on your windows form, and exactly which instance of System.Drawing.Graphics is throwing out, I can't really help further. That would be where I'd investigate further; try to discover the exact instance being disposed, what other object owns it, and why it's being finalized after already being disposed (usually if explicitly disposed, a call to GC.SuppressFinalize() should be made).

One of the "reflected" codebase references may help you out; look for System.Drawing.Graphics.Dispose(bool disposing), and browse for any line of code that would fail if run twice in a row. That may give you a hint.

于 2012-04-26T15:44:28.393 回答