我有一个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,所以我无法跟踪它。
对于它的价值,我的后台工作人员会做以下事情:
当应用程序(未明确使用System.Drawing)退出时,是否有人对System.Drawing中可能导致未处理的NullReferenceException的内容有任何想法?
答案 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),并浏览连续运行两次会失败的任何代码行。这可能会给你一个提示。