Control.EndInvoke重置异常的调用堆栈

时间:2010-04-12 11:59:14

标签: .net winforms exception asynchronous

我没有做很多Windows GUI编程,所以对于比我更熟悉WinForms的人来说,这可能都是常识。不幸的是,我无法找到任何资源来解释这个问题,我今天在调试过程中遇到过。

如果我们在异步委托上调用EndInvoke。我们将在重新抛出的方法执行期间抛出任何异常。调用堆栈将反映异常的原始来源。

但是,如果我们在Windows.Forms.Control上执行类似的操作,Control.EndInvoke的实现将重置调用堆栈。这可以通过简单的测试或查看Reflector中的代码来观察。 EndInvoke的相关代码摘录如下:

if (entry.exception != null)
{
   throw entry.exception;
}

我理解控件和异步委托上的Begin / EndInvoke是不同的,但我希望Control.EndInvoke上有类似的行为。

是否有任何理由控制不执行异步委托保留原始调用堆栈的任何操作?

4 个答案:

答案 0 :(得分:1)

我不知道真正的原因,但我可以猜测异步委托类似于RPC,而控制委托可能基于Win32消息发送。不同的技术因此这个功能的影响可能不一样。异步委托将从所有远程代码中受益,开发人员可以编写代码以在不同进程或计算机之间传输异常调用堆栈,而控制委托将在同一进程中使用PostMessage模拟RPC。不同的团队,不同的代码。

答案 1 :(得分:1)

另请注意,Control.EndInvoke是框架中为数不多的受管EndInvokes之一(因此您可以在Reflector中看到代码)。他们可能应该有一个非托管帮助程序,它会抛出原始堆栈。

事实上,我认为只有 托管EndInvoke,但还有其他托管End*例程,其IAsyncResult参数。我没有检查所有这些,但似乎所有我已经审查过的只是抛出异常,或者有效地使用Stephen Cleary转移使用.NET 4的GetWaiter.GetResult的解决方案,它确实有一些托管和非托管的恶作剧试图恢复异常堆栈。

答案 2 :(得分:1)

我不确定为什么Control不这样做(可能只是疏忽),但你可以通过将任务安排到UI表单来解决它在.NET 4.0中的问题:

    private BackgroundWorker bgw;
    private TaskFactory uiTaskFactory;

    private void Form1_Load(object sender, EventArgs e)
    {
        this.uiTaskFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
        this.bgw = new BackgroundWorker();
        this.bgw.DoWork += bgw_DoWork;
        this.bgw.RunWorkerAsync();
    }

    void bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        var task = this.uiTaskFactory.StartNew(this.OuterTaskFunction);
        try
        {
            task.Wait();
        }
        catch (Exception ex)
        {
            // Note: Full stack trace preserved
            MessageBox.Show(ex.InnerException.ToString());
        }
    }

    void OuterTaskFunction()
    {
        this.InnerTaskFunction();
    }

    void InnerTaskFunction()
    {
        throw new InvalidOperationException("Blah.");
    }

答案 3 :(得分:-3)

我没有100%阅读你的信息所以我不确定这是否有帮助,或者我只是说明显的事情, 但是当一个异常被抓住并且你写了

“throw iAmAnCaughtExceptionInstance;”

调用堆栈不会被保存,你应该写

“抛出;”

然后保存调用堆栈