WPF: Why BackgroundWorker.RunWorkerCompleted Event() throw an exception and main thread does not receive it?

Jane Jie Chen 466 Reputation points
2025-03-07T20:07:38.0433333+00:00

Our WPF application targets .NET 4.8.

The AbortableBackgroundWorkder class inherences from the BackgroundWorker class and used as following.

there are two background workers:

_protocolProcessorBackgroundWorker..DoWork() will call {

_protocolProcessorMacro.Execute(_protocolProcessorBackgroundWorker.Token);

}

_protocolProcessorBackgroundWorker.RunWorkerAsync();

_protocolProcessorMacro.Execute() will call {

pcrBackgroundWorker.DoWork += (, __) => { _pcrMacro.Execute(_pcrBackgroundWorker.Token); };

}

_pcrBackgroundWorker.RunWorkerAsync();

when _pcrMacro.Execute() has occurred an exception, it went through "_pcrBackgroundWorker.RunWorkerCompleted()" event with following black code.

// Exception occurred

                    if ( e.Error != null )

                    {

                        WriteToRunLog( "!! PCR Exception " + e.Error.Message );

                        throw e.Error;

                    }

However, "throw e.Error;" seems hidden and did not receive by ProtocolProcessorMacro catch block.

so ProtocolProcessorMacro continues to next macro "transferPcrMacro.Execute();"

what cause that "throw e.Error" hidden?

How to fix it so that ProtocolProcessorMacro catch block will catch the exception. Thx!

public class ProtocolProcessor
{
	public void Execute()       
	{
		try
        {
            //  Raise (and log) the Macro Started event
            MarkMacroStarted();
			
			// a lot instrument control sequence actions
			if ( _protocolProcessorParams.PerformPcrMacro )
                {
                    EvaluateMacroStopPoint( "PCR" );
                    WriteToRunLog( "!! Start Macro - PCR" );
                    _pcrBackgroundWorker = new AbortableBackgroundWorker();
                    _pcrBackgroundWorker.WorkerSupportsCancellation = true;
                    _pcrBackgroundWorker.DoWork += (_, __) => { _pcrMacro.Execute(_pcrBackgroundWorker.Token); };
                    _pcrBackgroundWorker.RunWorkerCompleted += ( sender, e ) =>
                    {
                        //  clear the background worker to indicate it done
                        _pcrBackgroundWorker = null;
                        if ( e.Cancelled )
                        {
                            WriteToRunLog( "!! PCR Aborted " );
                        }
                        else
                        //  Exception occurred
                        if ( e.Error != null )
                        {
                            WriteToRunLog( "!! PCR Exception " + e.Error.Message );
                            throw e.Error;
                        }
                        else
                        {
                            WriteToRunLog( "!! Finish Macro - PCR" );
                        }
                    };
                    _pcrBackgroundWorker.RunWorkerAsync();
                }

			if ( _protocolProcessorParams.PerformTransferPcrMacro )
                {
					 _transferPcrMacro.Execute(token);
				}
			.......

			EvaluateMacroStopPoint( "Macro Finished" );
		}
		catch( Exception ex )
        {
            WriteToRunLog( "!!! Exception Occurred in Macro !!! - " + ex.Message );
            stoppedByError = true;
            throw;
        }
        finally
        {
			// Thread.Sleep(200);  //is it safe to sleep in finally block?
            //  handle case where the macro was stopped by the user (not aborted)
            //  exception occurred, was logged and thrown again
			TurnOffAllHeatersAndStopLogging();  //include write sth into run log
            
		}
	}
}

private AbortableBackgroundWorker           _protocolProcessorBackgroundWorker  = null;
private ProtocolProcessor                   _protocolProcessorMacro             = null;

Public void BeginXXXRun()
{
	InstrumentControl.Instance.ExceptionThrown += OnInstrumentControlExceptionThrownDuringProtocol;

	_protocolProcessorBackgroundWorker = new AbortableBackgroundWorker();
    _protocolProcessorBackgroundWorker.WorkerSupportsCancellation = true;
    _protocolProcessorBackgroundWorker.DoWork += (_, __) =>
    {
		try
		{
		_protocolProcessorMacro = new ProtocolProcessor( );
        _protocolProcessorMacro.ProtocolProcessorRunEvent += OnProtocolProcessorRunEvent;
		
		_protocolProcessorMacro.PreExecute();
        _protocolProcessorMacro.Execute();
        _protocolProcessorMacro.PostExecute();
		}
		catch ( ThreadAbortException /* taEx */ )
        {
        }
        catch ( Exception ex )
        {
             GenerateInstrumentExceptionWarningOrFault( ex );
             throw;
        }
        finally
        {
        
               _protocolProcessorMacro.StopLoggingToFile();
        }
	};
	_protocolProcessorBackgroundWorker.RunWorkerCompleted += OnProtocolProcessorRunComplete;
    _protocolProcessorBackgroundWorker.RunWorkerAsync();
  }
}

}

C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
11,333 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Bruce (SqlWork.com) 72,026 Reputation points
    2025-03-07T23:21:48.22+00:00

    There is no code to catch the error. The Execute() method exits long before the error is thrown. Even if it had a sleep, it would not catch the error. You need to use a task wait or async/await to catch the error. note: one thread can not catch another’s error. You need check the status of the task after completion, to detect and rethink the error. This is what wait() and await do.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.