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,339 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Bruce (SqlWork.com) 72,276 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 re-throw the error. This is what wait() and await do.


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.