#Region " Option Statements "
Option Strict On
Option Explicit On
Option Infer Off
#End Region
#Region " Imports "
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Threading
#End Region
#Region " BackgroundWorker State "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Specifies the state of a <see cref="BackgroundWorker"/>.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Public Enum BackgroundWorkerState As Integer
''' <summary>
''' The <see cref="BackgroundWorker"/> is stopped.
''' </summary>
Stopped = 0
''' <summary>
''' The <see cref="BackgroundWorker"/> is running.
''' </summary>
Running = 1
''' <summary>
''' The <see cref="BackgroundWorker"/> is paused.
''' </summary>
Paused = 2
''' <summary>
''' The <see cref="BackgroundWorker"/> is pending on a cancellation.
''' </summary>
CancellationPending = 3
''' <summary>
''' The <see cref="BackgroundWorker"/> is completed (stopped).
''' </summary>
Completed = 4
End Enum
#End Region
#Region " My Background Work "
Public NotInheritable Class MyBackgroundWork : Implements IDisposable
#Region " Private Fields "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' The underliying <see cref="BackgroundWorker"/> instance that runs this work on background.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
<EditorBrowsable(EditorBrowsableState.Never)>
Friend WithEvents Worker As BackgroundWorker
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' A <see cref="ManualResetEvent"/> that serves to handle synchronous operations (Run, Pause, Resume, Cancel).
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Private ReadOnly mreSync As ManualResetEvent
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' A <see cref="ManualResetEvent"/> that serves to handle asynchronous operations (RunAsync, CancelAsync).
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Private ReadOnly mreAsync As ManualResetEvent
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Indicates whether <see cref="BackGroundworker"/> has been initiated in synchronous mode.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Private isRunSync As Boolean = False
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Indicates whether a synchronous cancellation operation is requested.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Private isCancelSyncRequested As Boolean = False
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Indicates whether a pause operation is requested.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Private isPauseRequested As Boolean = False
#End Region
#Region " Properties "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Gets the current state of the background work.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <value>
''' The current state of the background work.
''' </value>
''' ----------------------------------------------------------------------------------------------------
Public ReadOnly Property State As BackgroundWorkerState
<DebuggerStepThrough>
Get
Return Me.stateB
End Get
End Property
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' ( Backing Field )
''' The current state of the background work.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Private stateB As BackgroundWorkerState = BackgroundWorkerState.Stopped
#End Region
#Region " Constructors "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Initializes a new instance of the <see cref="MyBackgroundWork"/> class.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
<DebuggerNonUserCode>
Public Sub New()
Me.Worker = New BackgroundWorker()
Me.mreSync = New ManualResetEvent(initialState:=False)
Me.mreAsync = New ManualResetEvent(initialState:=True)
End Sub
#End Region
#Region " Public Methods "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Run the background work.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="InvalidOperationException">
''' In order to run the BackgroundWorker instance it must be stopped or completed.
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub Run()
If (Me.Worker Is Nothing) Then
Throw New ObjectDisposedException(objectName:="Worker")
Else
Select Case Me.stateB
Case BackgroundWorkerState.Stopped, BackgroundWorkerState.Completed
Me.isRunSync = True
With Me.Worker
.WorkerSupportsCancellation = False
.WorkerReportsProgress = False
.RunWorkerAsync()
End With
Me.stateB = BackgroundWorkerState.Running
Me.mreSync.WaitOne()
Case Else
Throw New InvalidOperationException("In order to run the BackgroundWorker instance it must be stopped or completed.")
End Select
End If
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Asynchronouslly run the background work.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="InvalidOperationException">
''' In order to run the BackgroundWorker instance it must be stopped or completed.
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub RunAsync()
If (Me.Worker Is Nothing) Then
Throw New ObjectDisposedException(objectName:="Worker")
Else
Select Case Me.stateB
Case BackgroundWorkerState.Stopped, BackgroundWorkerState.Completed
With Me.Worker
.WorkerSupportsCancellation = True
.WorkerReportsProgress = True
.RunWorkerAsync()
End With
Me.stateB = BackgroundWorkerState.Running
Case Else
Throw New InvalidOperationException("In order to run the BackgroundWorker instance it must be stopped or completed.")
End Select
End If
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Asynchronouslly pause the background work.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="InvalidOperationException">
''' In order to pause the BackgroundWorker instance it must be running.
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub PauseAsync()
If (Me.Worker Is Nothing) Then
Throw New ObjectDisposedException(objectName:="Worker")
Else
Select Case Me.stateB
Case BackgroundWorkerState.Running
Me.isPauseRequested = True
Me.stateB = BackgroundWorkerState.Paused
Me.mreAsync.Reset()
Case Else
Throw New InvalidOperationException("In order to pause the BackgroundWorker instance it must be running.")
End Select
End If
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Resume the background work.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="InvalidOperationException">
''' In order to resume the BackgroundWorker instance it must be paused.
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub [Resume]()
If (Me.Worker Is Nothing) Then
Throw New ObjectDisposedException(objectName:="Worker")
Else
Select Case Me.stateB
Case BackgroundWorkerState.Paused
Me.stateB = BackgroundWorkerState.Running
Me.isPauseRequested = False
Me.mreAsync.Set()
Case Else
Throw New InvalidOperationException("In order to resume the BackgroundWorker instance must be paused.")
End Select
End If
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Cancel the background work.
''' <para></para>
''' It blocks the caller thread until the remaining work is done.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="InvalidOperationException">
''' In order to cancel the BackgroundWorker instance it must be running or paused.
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub Cancel()
Me.isCancelSyncRequested = True
Me.CancelAsync()
Me.mreSync.WaitOne()
Me.isCancelSyncRequested = False
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Asynchronouslly cancel the background work.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="InvalidOperationException">
''' In order to cancel the BackgroundWorker instance it must be running or paused.
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub CancelAsync()
If (Me.Worker Is Nothing) Then
Throw New ObjectDisposedException(objectName:="Worker")
Else
Select Case Me.stateB
Case BackgroundWorkerState.CancellationPending
Exit Sub
Case BackgroundWorkerState.Running, BackgroundWorkerState.Paused
Me.mreAsync.Set() ' Resume thread if it is paused.
Me.stateB = BackgroundWorkerState.CancellationPending
Me.Worker.CancelAsync() ' Cancel it.
Case Else
Throw New InvalidOperationException("In order to cancel the BackgroundWorker instance must be running or paused.")
End Select
End If
End Sub
#End Region
#Region " Private Methods "
<DebuggerStepperBoundary>
Private Sub DoSomething()
Thread.Sleep(TimeSpan.FromSeconds(5))
End Sub
#End Region
#Region " Event-Handlers "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Handles the <see cref="BackgroundWorker.DoWork"/> event of the <see cref="Worker"/> instance.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <param name="sender">
''' The source of the event.
''' </param>
'''
''' <param name="e">
''' The <see cref="DoWorkEventArgs"/> instance containing the event data.
''' </param>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepperBoundary>
Private Sub Worker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) _
Handles Worker.DoWork
Dim progress As Integer
Dim lock As Object = ""
SyncLock lock
For i As Integer = 0 To 100
If (Me.Worker.CancellationPending) Then
e.Cancel = True
Exit For
Else
If (Me.isPauseRequested) Then ' Pause this thread right here.
Me.mreAsync.WaitOne(Timeout.Infinite)
End If
Me.DoSomething()
If Me.Worker.WorkerReportsProgress Then
progress = i
Me.Worker.ReportProgress(progress)
End If
End If
Next i
End SyncLock
If (Me.Worker.WorkerReportsProgress) AndAlso Not (Me.Worker.CancellationPending) AndAlso (progress < 100) Then
Me.Worker.ReportProgress(percentProgress:=100)
End If
If (Me.isRunSync) OrElse (Me.isCancelSyncRequested) Then
Me.mreSync.Set()
End If
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Handles the <see cref="BackgroundWorker.ProgressChanged"/> event of the <see cref="Worker"/> instance.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <param name="sender">
''' The source of the event.
''' </param>
'''
''' <param name="e">
''' The <see cref="ProgressChangedEventArgs"/> instance containing the event data.
''' </param>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepperBoundary>
Private Sub Worker_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _
Handles Worker.ProgressChanged
Console.WriteLine(String.Format("Work Progress: {0:00.00}%", e.ProgressPercentage))
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Handles the <see cref="BackgroundWorker.RunWorkerCompleted"/> event of the <see cref="Worker"/> instance.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <param name="sender">
''' The source of the event.
''' </param>
'''
''' <param name="e">
''' The <see cref="RunWorkerCompletedEventArgs"/> instance containing the event data.
''' </param>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepperBoundary>
Private Sub Worker_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) _
Handles Worker.RunWorkerCompleted
If (e.Cancelled) Then
Console.WriteLine("Background work cancelled.")
ElseIf (e.Error IsNot Nothing) Then
Console.WriteLine("Background work error.")
Else
Console.WriteLine("Background work done.")
End If
Me.stateB = BackgroundWorkerState.Completed
End Sub
#End Region
#Region " IDisposable Implementation "
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Flag to detect redundant calls when disposing.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
Private isDisposed As Boolean
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Releases all the resources used by this instance.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Sub Dispose() Implements IDisposable.Dispose
Me.Dispose(isDisposing:=True)
GC.SuppressFinalize(obj:=Me)
End Sub
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
''' <para></para>
''' Releases unmanaged and, optionally, managed resources.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <param name="isDisposing">
''' <see langword="True"/> to release both managed and unmanaged resources;
''' <see langword="False"/> to release only unmanaged resources.
''' </param>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Private Sub Dispose(ByVal isDisposing As Boolean)
If (Not Me.isDisposed) AndAlso (isDisposing) Then
If (Me.Worker IsNot Nothing) Then
Me.Worker.Dispose()
Me.Worker = Nothing
With Me.mreSync
.SafeWaitHandle.Close()
.Dispose()
End With
With Me.mreAsync
.SafeWaitHandle.Close()
.Dispose()
End With
Me.isRunSync = False
Me.stateB = BackgroundWorkerState.Stopped
End If
End If
Me.isDisposed = True
End Sub
#End Region
End Class
#End Region