Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / threads

Passing arguments to a threaded LongRunningProcess

0.00/5 (No votes)
30 Mar 2013CPOL1 min read 13.4K  
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.  

VB.NET
' 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)