类型为“System.ApplicationException”的未处理异常发生在System.Drawing.dll中。

20

我有一个WinForms应用程序。在开发模式下,当我从Visual Studio .NET 2003(是的,我知道它很老,但这是一个遗留项目)调试时,当我尝试打开一个新表单时,会出现以下错误。为了打开一个新表单,我获取表单实例,然后调用ShowDialog()方法,例如:

frmTest test = new frmTest(here my parameters);
test.ShowDialog();

如果我在调试时按下F11(步入),它不会崩溃,但如果我在实例化表单的行上按下F10进入下一行,也就是test.ShowDialog(),那么它就会崩溃并显示以下错误信息。
完整的错误消息如下:
"发生了一个未处理的异常类型'System.ApplicationException',在System.drawing.dll中。其他信息: 尝试释放不属于该进程的互斥对象"
我已经翻译了最后一部分:“Additional information...”,因为它出现在西班牙语中。
我使用参数实例化的表单,它的构造函数包括初始化一些变量,例如:
public frmTest(string param1, string param2)
{
   InitializeComponent();

   this.param1 = param1;
   this.param2 = param2;
}

private void frmTest_Load(object sender, EventArgs e)
{
    // here I call a remote webservice asynchronously.
}

此外,我的表单"frmTest"包含四个图片框,一个标签和一个按钮。其中三个图片框包含PNG图像(在设计时通过Image属性分配),最后一个图片框包含一个动画GIF,也是在设计时通过Image属性加载的。也许错误是由于这些图像引起的?


5
你可以尝试删除所有图片框的图像属性并运行程序。如果它能正常运行,那么就逐个设置图片框的属性并运行程序,以确定哪个图片框是问题的原因。 - Chetan
2
也许值得进一步跟踪,通过获取完整的堆栈跟踪并使用像JetBrains的免费dotPeek这样的反编译工具来查看.NET 1.1 dll的源代码,如果您无法像@III建议的那样进行移植。 - Dimitar Nikovski
10
这是一个相当奇怪的异常。但谁知道,ApplicationException是.NET 1.0的设计失误之一。这个问题需要一个更好的非翻译的异常消息和堆栈跟踪。但很可能是线程错误,.NET 1.x使得在没有合适的诊断的情况下很容易出错。使用调试器的Debug > Windows > Threads窗口查看发生了什么。 - Hans Passant
1
我认为你应该展示一下表单构造器中正在发生的事情,也许我们可以更好地帮助你。 - Liquid Core
1
看起来是线程安全问题。您的异步调用正在尝试将图片添加到占位符中。该占位符在主线程下运行。最好使用委托来添加图片。 - Rajeesh Madambat
显示剩余14条评论
4个回答

1
TL;DR: 您的Web请求处理程序将在不同的线程上执行。确保您不要在处理程序中执行任何不安全的操作。您可以使用Invoke将回调处理程序的代码分派到主线程。
诊断: 问题几乎肯定隐藏在您的异步调用的缺少细节中。
// here I call a remote webservice asynchronously.

异步这个词有点含糊不清,很难确定具体发生了什么,但非常有可能您正在使用的异步机制已经在不同的线程上执行了回调函数,而不是主UI线程。

概述

在.NET模型中,这是很常见的。 .NET模型中的异步I/O利用线程池中的线程通过I/O完成端口(IOCP)处理I/O。这意味着当像Socket.BeginReceiveWebRequest.BeginGetResponse(或任何使用类似技术的.NET异步Web请求)这样的调用完成时,回调将在线程池中的线程上执行,而不是主线程。这可能会让您感到惊讶,因为您并没有积极地创建另一个线程;您只是参与了进行异步调用。

在网络请求的回调中,您必须非常小心,因为许多用户界面/Windows Forms操作只允许在主UI线程上执行。同样,可能不是UI本身引起了问题,而是您可能访问了一些不安全的资源或对象。许多看似无害的事情如果您在多线程上不小心操作可能会导致崩溃或异常。

解决方案:

如果您有疑问,在您的回调函数中,尽早地将代码分派(又名Invoke)到处理程序中,以便在主线程上运行。

实现这个的常见模式如下所示。

假设您已经进行了如下调用:

IAsyncResult result = (IAsyncResult myHttpWebRequest.BeginGetResponse(
    new AsyncCallback(RespoCallback), myRequestState);

处理程序可能设置如下所示:
private static void RespCallback(IAsyncResult asynchronousResult)
{  
    // THIS IS NOT GOING TO WORK BECAUSE WE ARE ON THE WRONG THREAD.  e.g.:
    this.label1.Text = "OK";  // BOOM!  :(
}

相反,将任何必要的处理任务分派回主线程。

private static void RespCallback(IAsyncResult asynchronousResult)
{  
    this.Invoke((MethodInvoker) delegate { 
       // This block of code will run on the main thread.
       // It is safe to do UI things now.  e.g.:
       this.label1.Text = "OK";  // HOORAY!  :)
    });
}

不建议将这作为一般最佳实践。我不是说要立即将所有处理程序调度回主线程。并非一刀切。您应该仔细查看处理程序中的具体细节,并确保您不会执行特定于线程的操作。但是,如果您没有对异步处理程序正在执行的内容进行任何解释,那么问题很可能通过在主线程上调用处理程序代码来解决。

注意:当然,要使用此技术解决您的问题,需要确保您的主线程正在运行。如果您使用(糟糕的)技术阻塞了主线程,例如此示例中的技术,则必须重新设计应用程序的某些部分。以下是需要更大改造的示例:

// Start the asynchronous request.
IAsyncResult result=
  (IAsyncResult) myHttpWebRequest.BeginGetResponse(new AsyncCallback(RespCallback),myRequestState);

// this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted
ThreadPool.RegisterWaitForSingleObject (result.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), myHttpWebRequest, DefaultTimeout, true);

// The response came in the allowed time. The work processing will happen in the 
// callback function.
allDone.WaitOne(); // *** DANGER:  This blocks the main thread, the IO thread
                   // won't be able to dispatch any work to it via `invoke`

注意WaitOne的调用吗?这会阻塞执行线程。如果此代码在主线程上执行,则主线程将被阻塞,直到WebRequest完成。您需要重新设计,以便不阻塞主线程(我的建议),或更仔细地检查回调处理程序,以查看其所做的工作与其他线程冲突的原因。

0

可能异步调用正在尝试访问UI线程。

确保您没有使用控件属性,如 TextBox.Text。如果是这样,您只需将其值传递给异步调用,或在调用前将其存储在类变量中。

此外,在异步调用内部,您不能为该属性分配值。请改用 Invoke()


0

0

尝试添加异常断点,VS将停在引起异常的指令处。实际的堆栈跟踪可能会有所帮助。

您是否尝试关闭VS的本地变量监视窗口?也许它正在为您评估UI组件上的某些内容,其中访问线程应该等于UI组件的所有者线程!


2
为什么是-1?这两个都是有效的调试步骤!许多人不知道异常断点。许多人不关心实际值如何在本地变量监视窗口中可见。在VS执行我们代码的部分以显示值时,可能会导致不可预测的故障;特别是在WinForms控件中,hwnd创建线程非常重要。 - cly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接