C# BackgroundWorker.RunWorkCompleted-无法重试异常

C# BackgroundWorker.RunWorkCompleted-无法重试异常,c#,exception,backgroundworker,C#,Exception,Backgroundworker,我正在为WCF调用编写某种包装(使用BackgroundWorker),以防止调用进行时GUI冻结。它基本上正常工作,但当WCF调用引发异常时,我与BackgroundWorker有一个问题。如果DoWork中发生异常,我可以在RunWorkCompleted中检测到它,但将其重新提交到GUI不起作用。我读过很多文章,有人提到这应该是可行的 包装器的代码(请注意,WCF调用由抛出的异常表示): 调试时,我得到: DoWork中引发了“System.Exception”类型的异常 在抛出evt.E

我正在为WCF调用编写某种包装(使用BackgroundWorker),以防止调用进行时GUI冻结。它基本上正常工作,但当WCF调用引发异常时,我与BackgroundWorker有一个问题。如果DoWork中发生异常,我可以在RunWorkCompleted中检测到它,但将其重新提交到GUI不起作用。我读过很多文章,有人提到这应该是可行的

包装器的代码(请注意,WCF调用由抛出的异常表示):

调试时,我得到:

  • DoWork中引发了“System.Exception”类型的异常
  • 在抛出evt.Error时抛出“System.Exception”类型的异常
  • 主方法中的Application.Run(new Form1())处的“TargetInvocationException未处理”

  • 我做错了什么?我想在Windows窗体中捕获异常。

    事件
    b.RunWorkerCompleted
    是您应该执行错误处理的地方。您可以传入一个
    操作
    ,以执行如下错误处理

    private void GetSomething(Action<IEnumerable<int>> completedAction, Action<Exception> exceptionAction)
    {
        BackgroundWorker b = new BackgroundWorker();
    
        b.DoWork += (s, evt) => { throw new Exception(); evt.Result = new List<int> { 1, 2, 3 }; };
    
        b.RunWorkerCompleted += (s, evt) =>
        {
            if (evt.Error == null && completedAction != null)
                completedAction((IEnumerable<int>)evt.Result);
            else if(evt.Error != null)
                exceptionAction(evt.Error);
        };
    
        b.RunWorkerAsync();
    }
    
    如果您使用.Net 4.5和C#5(您需要VS2012或VS2010以及异步CTP),您甚至可以求助于
    Async
    wait

        private async void button3_Click(object sender, EventArgs e)
        {
            try
            {
                var list = await GetSomething();
                foreach (int i in list)
                {
                    listView1.Items.Add(new ListViewItem(i.ToString()));
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    

    。。。所有的魔法都是由编译器完成的。请注意,您可以按习惯使用
    try
    catch

    您应该更改此选项:

    抛出evt.Error

    为此:

    MessageBox.Show(evt.Error.Message)


    您的异常当前未处理,因为
    RunWorkerCompleted
    处理程序稍后正在运行。它没有在您在
    按钮3\u Click

    中的try/catch中运行。您应该在RunWorkerCompleted中处理异常。是否有任何特定的原因让您重试。@CodeIgnoto:我的想法是将此决定推给调用方。我不想使用消息框,比如说,从这个包装。根据我的经验,任何在RunWorkerCompleted代码中抛出的异常都会导致应用程序停止运行。所以,去完成任务或者在那里处理它。如果您在启动时使用while(true)Application.DoEvents()而不是Application.Run(),则可以对DoEvents执行try catch,它将捕获您刚刚重试的异常。但是有些代码可能不会像预期的那样使用这种“消息”循环运行。我现在试过了。第一个问题是“foreach(列表中的inti)”不会编译,因为GetSomething的返回类型是一个任务。将其更改为迭代task.Result后,我的GUI在调用过程中被阻止,并且未捕获异常。抱歉。你完全正确。带有
    ContinueWith
    的示例毫无意义。我纠正了它。但是,如果使用
    Task.Factory.StartNew()
    ,则不应阻止GUI。我将尝试此操作,谢谢。事实上,我认为你的第一个解决方案是通过一个动作。这将从GUI中抽象出许多细节。
    private void GetSomething(Action<IEnumerable<int>> completedAction, Action<Exception> exceptionAction)
    {
        BackgroundWorker b = new BackgroundWorker();
    
        b.DoWork += (s, evt) => { throw new Exception(); evt.Result = new List<int> { 1, 2, 3 }; };
    
        b.RunWorkerCompleted += (s, evt) =>
        {
            if (evt.Error == null && completedAction != null)
                completedAction((IEnumerable<int>)evt.Result);
            else if(evt.Error != null)
                exceptionAction(evt.Error);
        };
    
        b.RunWorkerAsync();
    }
    
    Task<IEnumerable<int>> GetSomething()
    {
        return Task.Factory.StartNew(() => { 
            Thread.Sleep(2000);
            throw new Exception(); 
            return (new List<int> { 1, 2, 3 }).AsEnumerable(); 
            });
    }
    
        private void button3_Click(object sender, EventArgs e)
        {
            GetSomething()
                .ContinueWith(task =>
                    {
                        if (task.IsCanceled)
                        {
                        }
                        else if (task.IsFaulted)
                        {
                            var ex = task.Exception.InnerException;
                            MessageBox.Show(ex.Message);
                        }
                        else if (task.IsCompleted)
                        {
                            var list = task.Result;
                            foreach (int i in list)
                            {
                                listView1.Items.Add(new ListViewItem(i.ToString()));
                            }
                        }
                    });
        }
    
        private async void button3_Click(object sender, EventArgs e)
        {
            try
            {
                var list = await GetSomething();
                foreach (int i in list)
                {
                    listView1.Items.Add(new ListViewItem(i.ToString()));
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }