Imports System.Threading
Namespace System.Windows.Forms
''' <summary>
''' you can pass Delegates together with suitable arguments to the methods RunAsync(),
''' NotifyGui() or ReportProgress(), and the Delegate will be executed either in a
''' side-thread or in the Gui-thread
''' </summary>
Public Class AsyncWorker
Private _ReportNext As Integer
Private _ReportInterval As Integer = 300
Private _TargetControl As Control
Private _CancelRequested As Integer = 0
Private _CancelNotified As Boolean
Private _IsRunning As Boolean
Public Event IsRunningChanged As EventHandler
Public ReadOnly Property CancelRequested() As Boolean
Get
Return Thread.VolatileRead(_CancelRequested) <> 0
End Get
End Property
Public Sub Cancel()
Thread.VolatileWrite(_CancelRequested, 1)
End Sub
Public ReadOnly Property IsRunning() As Boolean
Get
Return _IsRunning
End Get
End Property
Private Sub OnIsRunningChanged(ByVal value As Boolean)
_IsRunning = value
RaiseEvent IsRunningChanged(Me, EventArgs.Empty)
End Sub
Private Sub PreRun(ByVal actionTarget As Object)
_TargetControl = TryCast(actionTarget, Control)
If _IsRunning Then Throw Me.BugException("I'm already running")
_CancelRequested = 0
_CancelNotified = False
OnIsRunningChanged(True)
_ReportNext = Environment.TickCount
End Sub
Private Sub Callback(ByVal ar As IAsyncResult)
DirectCast(ar.AsyncState, AsyncCallback)(ar)
'this calls asyncAction.EndInvoke(ar)
InvokeGui(New Action(Of Boolean)(AddressOf OnIsRunningChanged), False)
End Sub
Private Function NotifyCancel() As Boolean
If _CancelNotified Then Throw Me.BugException( _
"after cancelation you should notify Gui no longer.")
_CancelNotified = True
End Function
Private Function ProgressInvoke( _
ByVal action As [Delegate], ByVal ParamArray args As Object()) As Boolean
If Thread.VolatileRead(_CancelRequested) <> 0 Then Return NotifyCancel()
Dim ticks = Environment.TickCount
If ticks < _ReportNext Then Return True
InvokeGui(action, args)
_ReportNext = ticks + _ReportInterval - ((ticks - _ReportNext) Mod _ReportInterval)
Return True
End Function
Private Function TryInvokeGui( _
ByVal action As [Delegate], ByVal ParamArray args As Object()) As Boolean
If Thread.VolatileRead(_CancelRequested) <> 0 Then Return NotifyCancel()
InvokeGui(action, args)
Return True
End Function
Private Sub InvokeGui(ByVal action As [Delegate], ByVal ParamArray args As Object())
If Application.OpenForms.Count = 0 _
OrElse (_TargetControl IsNot Nothing AndAlso _TargetControl.IsDisposed) Then
Cancel()
Else
Application.OpenForms(0).BeginInvoke(action, args)
End If
End Sub
''' <summary>
''' executes the Delegate in the Gui-thread, if the last progress-report is more
''' than <see cref=" _ReportInterval" />ago, and the process isn't canceled
''' </summary>
Public Function ReportProgress(ByVal psAction As Action) As Boolean
Return ProgressInvoke(psAction)
End Function
Public Function ReportProgress(Of T1)(ByVal psAction As Action(Of T1), ByVal arg1 As T1) As Boolean
Return ProgressInvoke(psAction, arg1)
End Function
Public Function ReportProgress(Of T1, T2)( _
ByVal psAction As Action(Of T1, T2), ByVal arg1 As T1, ByVal arg2 As T2) As Boolean
Return ProgressInvoke(psAction, arg1, arg2)
End Function
Public Function ReportProgress(Of T1, T2, T3)(ByVal psAction As Action(Of T1, T2, T3), _
ByVal arg1 As T1, ByVal arg2 As T2, ByVal arg3 As T3) As Boolean
Return ProgressInvoke(psAction, arg1, arg2, arg3)
End Function
Public Function ReportProgress(Of T1, T2, T3, T4)(ByVal psAction As Action(Of T1, T2, T3, T4), _
ByVal arg1 As T1, ByVal arg2 As T2, ByVal arg3 As T3, ByVal arg4 As T4) As Boolean
Return ProgressInvoke(psAction, arg1, arg2, arg3, arg4)
End Function
''' <summary>
''' executes the Delegate in the Gui-thread, if the process isn't canceled
''' </summary>
Public Function NotifyGui(ByVal syncAction As Action) As Boolean
Return TryInvokeGui(syncAction)
End Function
Public Function NotifyGui(Of T1)(ByVal syncAction As Action(Of T1), ByVal arg1 As T1) As Boolean
Return TryInvokeGui(syncAction, arg1)
End Function
Public Function NotifyGui(Of T1, T2)( _
ByVal syncAction As Action(Of T1, T2), ByVal arg1 As T1, ByVal arg2 As T2) As Boolean
Return TryInvokeGui(syncAction, arg1, arg2)
End Function
Public Function NotifyGui(Of T1, T2, T3)(ByVal syncAction As Action(Of T1, T2, T3), _
ByVal arg1 As T1, ByVal arg2 As T2, ByVal arg3 As T3) As Boolean
Return TryInvokeGui(syncAction, arg1, arg2, arg3)
End Function
Public Function NotifyGui(Of T1, T2, T3, T4)(ByVal syncAction As Action(Of T1, T2, T3, T4), _
ByVal arg1 As T1, ByVal arg2 As T2, ByVal arg3 As T3, ByVal arg4 As T4) As Boolean
Return TryInvokeGui(syncAction, arg1, arg2, arg3, arg4)
End Function
''' <summary>executes the Delegate in a thread of the threadpool</summary>
Public Sub RunAsync(ByVal asyncAction As Action)
PreRun(asyncAction.Target)
asyncAction.BeginInvoke( _
AddressOf Callback, New AsyncCallback(AddressOf asyncAction.EndInvoke))
End Sub
Public Sub RunAsync(Of T)(ByVal asyncAction As Action(Of T), ByVal arg As T)
PreRun(asyncAction.Target)
asyncAction.BeginInvoke( _
arg, AddressOf Callback, New AsyncCallback(AddressOf asyncAction.EndInvoke))
End Sub
Public Sub RunAsync(Of T1, T2)( _
ByVal asyncAction As Action(Of T1, T2), ByVal arg1 As T1, ByVal arg2 As T2)
PreRun(asyncAction.Target)
asyncAction.BeginInvoke( _
arg1, arg2, AddressOf Callback, New AsyncCallback(AddressOf asyncAction.EndInvoke))
End Sub
Public Sub RunAsync(Of T1, T2, T3)(ByVal asyncAction As Action(Of T1, T2, T3), _
ByVal arg1 As T1, ByVal arg2 As T2, ByVal arg3 As T3)
PreRun(asyncAction.Target)
asyncAction.BeginInvoke(arg1, arg2, arg3, AddressOf Callback, _
New AsyncCallback(AddressOf asyncAction.EndInvoke))
End Sub
Public Sub RunAsync(Of T1, T2, T3, T4)(ByVal asyncAction As Action(Of T1, T2, T3, T4), _
ByVal arg1 As T1, ByVal arg2 As T2, ByVal arg3 As T3, ByVal arg4 As T4)
PreRun(asyncAction.Target)
asyncAction.BeginInvoke(arg1, arg2, arg3, arg4, AddressOf Callback, _
New AsyncCallback(AddressOf asyncAction.EndInvoke))
End Sub
End Class
End Namespace