C# .NET WinForms应用程序在退出时崩溃,系统未经处理。NullReferenceException
我有一个WinForms应用程序,它包含一个带有后台工作程序的表单。表单包含一个通过RunWorkerAsync()启动后台工作程序的按钮和另一个将退出应用程序的按钮。大约有三分之一的时间,在后台工作人员完成其工作后,在我单击退出按钮后,应用程序将崩溃,例外情况如下:C# .NET WinForms应用程序在退出时崩溃,系统未经处理。NullReferenceException,c#,.net,winforms,backgroundworker,unhandled-exception,C#,.net,Winforms,Backgroundworker,Unhandled Exception,我有一个WinForms应用程序,它包含一个带有后台工作程序的表单。表单包含一个通过RunWorkerAsync()启动后台工作程序的按钮和另一个将退出应用程序的按钮。大约有三分之一的时间,在后台工作人员完成其工作后,在我单击退出按钮后,应用程序将崩溃,例外情况如下: System.NullReferenceException was unhandled Message=Object reference not set to an instance of an object. Sourc
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中出现未处理的NullReferenceException吗?当应用程序(不显式使用System.Drawing)退出时进行绘图?而您的程序可能不使用System。显式绘图时,大多数WinForms对象代码中都存在该命名空间。似乎在表单的某个地方,一个GUI对象被赋予了对另一个对象的图形句柄的引用,然后它认为必须销毁该句柄;但是,拥有该句柄的控件已被释放,或者图形对象本身已显式调用其Dispose方法,因此Dispose()代码在第二次运行时失败。我本希望.NET开发人员进行检查,以确保双重处置不会成为问题,但在本例中,他们似乎忽视了这一点 如果不知道您的windows窗体上到底有什么,也不知道System.Drawing.Graphics的哪个实例被丢弃了,我真的无法进一步提供帮助。那将是我进一步调查的地方;尝试发现正在被释放的确切实例、拥有它的其他对象以及为什么在已经被释放后它会被最终确定(通常,如果显式地被释放,则应该调用GC.SuppressFinalize()
其中一个“反射的”代码库引用可能会帮助您;查找System.Drawing.Graphics.Dispose(bool disposing),并浏览如果连续运行两次会失败的任何代码行。这可能会给你一个提示。尽管基思的答案原则上是正确的,但你的代码似乎确实存在一个明显的问题。您正在调用
this.Close()代码>在调用CancelAsync()之后。我会尝试等待后台工作人员完成其业务,或者订阅已取消的事件(如果存在)
很可能您的后台工作人员尚未完成。还请注意,后台工作程序是一个与窗体的事件继承人权限相关联的组件
尝试创建新任务:
this.BeginInvoke(新操作(()=>(Thread.Sleep(1000);this.Close();))代码>
很抱歉语法不正确,但我不在开发人员机器上。发布完整的堆栈跟踪,请接受建议,Raheel。对于在CancelAsync()之后立即调用Close()的代码路径,您肯定提出了一个有效的观点。不幸的是,这不是我到目前为止一直在执行的代码路径。后台工作程序仍在运行时,我尚未关闭应用程序。在我的backgroundWorker\u RunWorkerCompleted()方法(此处未显示)中,我将buttonStartWorker.Enabled设置为true,这将导致buttonNexit\u Click()中的else子句被执行。我通过在buttonExit\u Click()的第一行上放置断点来验证这一点。