Passing arguments to a threaded LongRunningProcess





0/5 (0 vote)
Implement a progress bar and pass arguments to a LongRunningProcess.
Introduction
This article is an example of creating a new thread and passing arguments to a
LongRunningProcess
while updating a progress bar on the main thread.
Background
This article is a follow up to an article by this title published 4 May 2011. The original article was an attempt to find a solution. The current article is a final solution that has been tested for about six months.
Using the code
The code is slightly abstracted but should run when modified for the parameters of a specific process. In this example the process encrypts file1 to file2 and requires three additional parameters as inputs. The process returns 0 if there are no errors and a nonzero value if there is an error. The example code provides for a cancel button. This code is applicable for very large files and when a process does not provide for intermediate returns to update a progress bar. The code is divided into two classes: a main thread class and a thread wrapper which provides for starting a new thread and returning a completion value. An additional class includes a thread wrapper for getting the hash of a file. This is included to demonstrate returning data instead of an integer completion value.
' This code example demonstrates implementing a progress bar for a long
' running process by creating a new thread for the process while
' updating the progress bar on the main thread. In this example the
' long running process is an API call that does not return until
' finished and returns an integer to indicate an error or success.
Option Strict Off
Option Explicit On
Imports VB = Microsoft.VisualBasic
Imports CryptoSysAPI
Imports System
Imports System.IO
Imports System.Threading
Public Delegate Sub CallbackToEnc(ByVal var1 As Integer)
Public Delegate Sub CallbackToHash(ByVal fileHash As String)
Public Class Protect
Public rtnBlfResult As Integer
Public Shared Sub ResultCallback(ByVal var1 As Integer)
CProtect.rtnBlfResult = var1
End Sub
' This function encrypts file1 to file2. This example is a much simplified version
' of an encrypter and does not include what is necessary to prepare file1 for
' encryption.
Public Function Encrypter(ByVal file1 As String, _
ByVal file2 As String, _
ByVal param1 As String, _
ByVal param2 As Integer, _
ByVal param3 As String) As Short
Dim iAbortLock As Boolean = False ' Do not allow more than one call to cancel.
Dim time1 As Single = 0
Dim time2 As Single = 0
' Create a new thread for encrypting file1 to file2 where the encryption is a
' long running process.
' Thread wrapper runs new thread tw1, while progress bar is updated on
' the main thread.
CProtect.rtnBlfResult = -2 ' Initialize return.
Dim tw1 As New TWrapEnc(file1, _
file2, _
param1, _
param2, _
param3, _
New CallbackToEnc(AddressOf ResultCallback))
tw1.start()
' Update progress bar until tw1 returns or cancel button is clicked.
T1: time2 = VB.Timer
If cancelBttnSequence = 3 Then ' Indicates the user has canceled.
' Allow one thread abort call and then wait for thread to stop.
If iAbortLock = False Then
tw1.thread.Abort()
iAbortLock = True
time1 = VB.Timer
End If
' If thread stopped, stop waiting.
If tw1.thread.ThreadState = System.Threading.ThreadState.Stopped _
Then GoTo T2
' If wait is more than 5 seconds, exit. Timer gets time since midnight.
If time1 > 0 And (VB.Timer - time1) > 5 Then GoTo T2a
' If midnight is spanned, elapsed time will be negative.
If (VB.Timer - time1) < 0 Then time1 = VB.Timer ' Midnight problem, time1.
End If
If (VB.Timer - time2) < 0 Then time2 = VB.Timer ' Midnight problem, time2.
' Update Progress Bar every 1 seconds.
While (VB.Timer - time2) < 1
' Synclock synchronizes writing and reading of rtnBlfResult.
SyncLock tw1.rtnLockEnc
If CProtect.rtnBlfResult = -1 Then GoTo T2
End SyncLock
End While
UpdatePgrBar() ' Update progress bar.
GoTo T1
T2: tw1.join() ' Process has completed and tw1 has returned.
' If T2a executes without T2 executing, rtnBlfResult will be as preset (-2).
' This will be interpreted as an error in code that follows.
T2a: tw1 = Nothing
rtnBlfResult = CProtect.rtnBlfResult
If rtnBlfResult = 0 Then
Encrypter = -1 ' Successfull, file2 contains encrypted form of file1.
ElseIf iAbortLock = True Then
Encrypter = -2 ' Cancel.
Else
Encrypter = 0 ' Error.
End If
End Function
Public Shared Sub UpdatePgrBar()
' Some code to update progress bar.
End Sub
End Class
' This class is the thread wrapper.
Public Class TWrapEnc
Private fileX1 As String
Private fileX2 As String
Private paramX1 As String
Private paramX2 As Integer
Private paramX3 As String
Private blfRtnParam As Integer = -1
Private callBack As CallbackToEnc
Public rtnLockEnc As New Object
Public thread As System.Threading.Thread
Public Sub New(ByVal file1 As String, _
ByVal file2 As String, _
ByVal param1 As String, _
ByVal param2 As Integer, _
ByVal param3 As String, _
ByVal callBackDelegate As CallbackToEnc)
fileX1 = file1
fileX2 = file2
paramX1 = param1
paramX2 = param2
paramX3 = param3
callBack = callBackDelegate
thread = New System.Threading.Thread(AddressOf body)
End Sub
Public Sub start()
Me.thread.Start()
End Sub
Public Sub join()
Me.thread.Join()
End Sub
Public Sub body()
On Error GoTo E1
' API call to encrypt fileX1 to fileX2.
Me.blfRtnParam = Blowfish.FileEncrypt(Me.fileX1, Me.fileX2, _
Me.paramX1, Me.paramX2, _
Me.paramX3)
E1: SyncLock rtnLockEnc
If Not callBack Is Nothing Then callBack(Me.blfRtnParam)
End SyncLock
' Wipe some params.
Me.fileX1 = ""
Me.fileX2 = ""
Me.paramX1 = ""
End Sub
End Class
' This class is the thread wrapper for getting hash of fileNameParam.
' There is no call to this class from the Protect Class in this example. The
' class is included to show the return of data instead of an integer return.
Public Class ThreadWrapperHash
Private fileNameParam As String
Private callback As CallbackToHash
Private fileHashRtnParam As String
Public rtnLockHash As New Object
Public thread As System.Threading.Thread
Public Sub New(ByVal fileName As String, _
ByVal callbackDelegate As CallbackToHash)
fileNameParam = fileName
callback = callbackDelegate
thread = New System.Threading.Thread(AddressOf Body)
End Sub
Public Sub start()
Me.thread.Start()
End Sub
Public Sub join()
Me.thread.Join()
End Sub
Public Sub Body()
On Error GoTo CallB
Me.fileHashRtnParam = Sha256.FileHexHash(Me.fileNameParam)
CallB: SyncLock rtnLockHash
If Not callback Is Nothing Then callback(Me.fileHashRtnParam)
End SyncLock
Me.fileNameParam = ""
End Sub
End Class
Points of Interest
Thank you Marcus Kramer and SAKryukov for help on my initial attempts at this problem.
History
- First attempts at a solution published 4 May 2011.