File: System\ComponentModel\BackgroundWorker.cs
Web Access
Project: src\src\libraries\System.ComponentModel.EventBasedAsync\src\System.ComponentModel.EventBasedAsync.csproj (System.ComponentModel.EventBasedAsync)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
 
namespace System.ComponentModel
{
    public class BackgroundWorker : Component
    {
        // Private instance members
        private bool _canCancelWorker;
        private bool _workerReportsProgress;
        private bool _cancellationPending;
        private bool _isRunning;
        private AsyncOperation? _asyncOperation;
        private readonly SendOrPostCallback _operationCompleted;
        private readonly SendOrPostCallback _progressReporter;
 
        public BackgroundWorker()
        {
            _operationCompleted = new SendOrPostCallback(AsyncOperationCompleted!);
            _progressReporter = new SendOrPostCallback(ProgressReporter!);
        }
 
        private void AsyncOperationCompleted(object arg)
        {
            _isRunning = false;
            _cancellationPending = false;
            OnRunWorkerCompleted((RunWorkerCompletedEventArgs)arg);
        }
 
        public bool CancellationPending
        {
            get
            {
                return _cancellationPending;
            }
        }
 
        public void CancelAsync()
        {
            if (!WorkerSupportsCancellation)
            {
                throw new InvalidOperationException(SR.BackgroundWorker_WorkerDoesntSupportCancellation);
            }
 
            _cancellationPending = true;
        }
 
        public event DoWorkEventHandler? DoWork;
 
        public bool IsBusy
        {
            get
            {
                return _isRunning;
            }
        }
 
        protected virtual void OnDoWork(DoWorkEventArgs e)
        {
            DoWork?.Invoke(this, e);
        }
 
        protected virtual void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e)
        {
            RunWorkerCompleted?.Invoke(this, e);
        }
 
        protected virtual void OnProgressChanged(ProgressChangedEventArgs e)
        {
            ProgressChanged?.Invoke(this, e);
        }
 
        public event ProgressChangedEventHandler? ProgressChanged;
 
        // Gets invoked through the AsyncOperation on the proper thread.
        private void ProgressReporter(object arg)
        {
            OnProgressChanged((ProgressChangedEventArgs)arg);
        }
 
        // Cause progress update to be posted through current AsyncOperation.
        public void ReportProgress(int percentProgress)
        {
            ReportProgress(percentProgress, null);
        }
 
        // Cause progress update to be posted through current AsyncOperation.
        public void ReportProgress(int percentProgress, object? userState)
        {
            if (!WorkerReportsProgress)
            {
                throw new InvalidOperationException(SR.BackgroundWorker_WorkerDoesntReportProgress);
            }
 
            ProgressChangedEventArgs args = new ProgressChangedEventArgs(percentProgress, userState);
 
            if (_asyncOperation != null)
            {
                _asyncOperation.Post(_progressReporter, args);
            }
            else
            {
                _progressReporter(args);
            }
        }
 
        public void RunWorkerAsync()
        {
            RunWorkerAsync(null);
        }
 
        public void RunWorkerAsync(object? argument)
        {
            if (_isRunning)
            {
                throw new InvalidOperationException(SR.BackgroundWorker_WorkerAlreadyRunning);
            }
 
            _isRunning = true;
            _cancellationPending = false;
 
            _asyncOperation = AsyncOperationManager.CreateOperation(null);
            Task.Factory.StartNew(
                        WorkerThreadStart,
                        argument,
                        CancellationToken.None,
                        TaskCreationOptions.DenyChildAttach,
                        TaskScheduler.Default
                    );
        }
 
        public event RunWorkerCompletedEventHandler? RunWorkerCompleted;
 
        public bool WorkerReportsProgress
        {
            get
            {
                return _workerReportsProgress;
            }
 
            set
            {
                _workerReportsProgress = value;
            }
        }
 
        public bool WorkerSupportsCancellation
        {
            get
            {
                return _canCancelWorker;
            }
 
            set
            {
                _canCancelWorker = value;
            }
        }
 
        private void WorkerThreadStart(object? argument)
        {
            Debug.Assert(_asyncOperation != null, "_asyncOperation not initialized");
 
            object? workerResult = null;
            Exception? error = null;
            bool cancelled = false;
 
            try
            {
                DoWorkEventArgs doWorkArgs = new DoWorkEventArgs(argument);
                OnDoWork(doWorkArgs);
                if (doWorkArgs.Cancel)
                {
                    cancelled = true;
                }
                else
                {
                    workerResult = doWorkArgs.Result;
                }
            }
            catch (Exception exception)
            {
                error = exception;
            }
 
            var e = new RunWorkerCompletedEventArgs(workerResult, error, cancelled);
            _asyncOperation.PostOperationCompleted(_operationCompleted, e);
        }
 
        protected override void Dispose(bool disposing)
        {
        }
    }
}