BackgroundWorker RunWorkerCompleted in a Component
I'm familiar with the following:
"If the operation raises an exception that your code does not handle, the BackgroundWorker catches the exception and passes it into the RunWorkerCompleted event handler, where it is exposed as the Error property of System.ComponentModel.RunWorkerCompletedEventArgs. If you are running under the Visual Studio debugger, the debugger will break at开发者_如何学JAVA the point in the DoWork event handler where the unhandled exception was raised."
However, I've encountered a weird glitch.
In my component, there's an instance of BackgroundWorker.
Even though it's not running in the debugger, the exception remains unhandled by the worker.
Even simplified code produces an unhandled exception (and RunWorkerCompleted doesn't fire):
Throw New ArgumentException("Test")
The main thing is the code of RunWorkerComplete:
RaiseEvent UpdateComplete(Me, New AsyncCompletedEventArgs(e.Error, e.Cancelled, e.Result))
I need the component to expose the worker exception through a public event.
If I remove the RaiseEvent call, the exception becomes handled by the worker, and accessible through e.Error.
Apparently, raising an event causes the worker to miss the exception. How can that be?
Full Code:
Component:
Private Sub workerDownloader_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs)
RaiseEvent UpdateComplete(Me, New AsyncCompletedEventArgs(e.Error, e.Cancelled, e.Result))
End Sub
Private Sub workerDownloader_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs)
Throw New ArgumentException("Test")
End Sub
Host app (WinForms):
Private Sub Connector1_UpdateComplete(ByVal sender As System.Object, ByVal e As System.ComponentModel.AsyncCompletedEventArgs) Handles Connector1.UpdateComplete
If e.Error IsNot Nothing Then MessageBox.Show(e.Error.ToString)
End Sub
It could be that your e.Result
is throwing its own exception.
From the MSDN documentation on RunWorkerCompletedEventArgs
:
Your
RunWorkerCompleted
event handler should always check theError
andCancelled
properties before accessing theResult
property. If an exception was raised or if the operation was canceled, accessing theResult
property raises an exception.
Specifically, it will raise a TargetInvocationException
.
In the code you've posted, you are constructing a new AsyncCompletedEventArgs
object from the argument passed through your RunWorkerCompleted
event. I am not sure what your rationale for this is, but it looks to me like an unnecessary step since RunWorkerCompletedEventArgs
inherits from AsyncCompletedEventArgs
-- and so you can just pass your e
(a RunWorkerCompletedEventArgs
object) to your UpdateComplete
event:
Private Sub workerDownloader_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
RaiseEvent UpdateComplete(Me, e)
End Sub
Now, here's why this might fix your problem (and why the MSDN link I posted is relevant): the way you're currently doing it, you are accessing e.Result
in your call to the constructor for AsyncCompletedEventArgs
. By accessing the property there, before first checking e.Error
, you are creating a scenario where an exception is thrown in the process of evaluating the parameters to pass to UpdateComplete
. Because of this exception, your RaiseEvent
line cannot finish what it's doing; hence, your UpdateComplete
event is not being raised.
Update based on your updates to the question... I've mocked this up in C# code and it looks like it's a (cross thread?) issue trying to pass the Result from the event args to the AsyncCompletedEventArgs.
Passing the error and the cancel works, but when I try to pass the result object, it dies. Do you need the result object in the event?
UpdateComplete(this, new AsyncCompletedEventArgs(e.Error, e.Cancelled, null));
精彩评论