系统.绘图中发生类型为"System.ApplicationException"的未处理异常.dll



我有一个winforms应用程序。在开发模式下,从 Visual Studio .NET 2003 进行调试时(是的,我知道它很旧,但这是一个旧项目),当我尝试打开新窗体时出现此错误。为了打开一个新表单,我得到了表单的一个实例,然后调用 ShowDialog() 方法,例如:

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

如果我在调试时按 F11(单步执行),它不会崩溃,但如果在我实例化表单的行中,我按 F10 进入下一行,即测试。ShowDialog(),然后它崩溃显示此错误。

完整的消息错误是:

"发生了类型为'系统.应用程序异常'的未处理异常 在系统绘图.dll中。附加信息:尝试 释放不属于进程的相互排斥">

我已经翻译了最后一部分:附加信息...因为它以西班牙语出现。

我用参数实例化的形式,它的构造函数,包括初始化一些变量,例如:

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 属性加载。也许错误是由于这些图像而发生的?

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 请求)等调用完成时,回调将在线程池中的线程上执行,而不是在主线程上执行。 这可能会让您感到惊讶,因为您没有主动创建另一个线程;您刚刚参与了异步调用。

您必须非常小心在 Web 请求的回调中执行的操作,因为不允许在主 UI 线程以外的任何线程上执行许多用户界面/Windows 窗体操作。 同样,可能不是 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 完成。 您必须重新设计,以便您不会阻塞主线程(我的建议),或者更仔细地检查您的回调处理程序,以了解为什么它正在执行的操作与其他线程冲突。

应用程序异常不是由框架本身引发的:what-is-applicationexception-for-in-net;问题应该出在你没有框架的代码上。此外,请务必在执行操作之前检查"InvokeRequired"属性,如果是,请使用"Invoke"方法运行该方法。可以为此检查 c-sharp-cross-thread-call-problem。

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

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

此外,在异步调用中,不能为该属性赋值。请改用Invoke()

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

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

相关内容

  • 没有找到相关文章

最新更新