VERSION 1.0 CLASS BEGIN MultiUse = -1 'True Persistable = 0 'NotPersistable DataBindingBehavior = 0 'vbNone DataSourceBehavior = 0 'vbNone MTSTransactionMode = 0 'NotAnMTSObject END Attribute VB_Name = "ThreadControl" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = False Attribute VB_Exposed = False '*************************************************************** ' (c) Copyright 2000 Matthew J. Curland ' ' This file is from the CD-ROM accompanying the book: ' Advanced Visual Basic 6: Power Techniques for Everyday Programs ' Author: Matthew Curland ' Published by: Addison-Wesley, July 2000 ' ISBN: 0-201-70712-8 ' http://www.PowerVB.com ' ' You are entitled to license free distribution of any application ' that uses this file if you own a copy of the book, or if you ' have obtained the file from a source approved by the author. You ' may redistribute this file only with express written permission ' of the author. ' ' This file depends on: ' References: ' VBoostTypes6.olb (VBoost Object Types (6.0)) ' ThreadAPI.olb (VBoost: API declares used for threading) ' VBoost6.Dll (VBoost Object Implementation (6.0)) (optional) ' Files: ' ThreadData.bas ' ThreadLaunch.cls ' ThreadProc.bas ' VBoost.bas ' Minimal VBoost conditionals: ' VBOOST_INTERNAL = 1 : VBOOST_CUSTOM = 1 ' Conditional Compilation Values: ' NOVBOOST = 1 'Removes VBoost.bas dependency ' ' This file is discussed in Chapter 13. '*************************************************************** Option Explicit Private m_RunningThreads As Collection 'Collection to hold ThreadData objects for each running thread Private m_FinishedThreads As Collection 'Collection to hold ThreadData objects for each finished thread Private m_CS As ThreadingAPIs.CRITICAL_SECTION 'Critical section to avoid conflicts when signalling threads Private m_pCS As Long 'Pointer to m_CS structure 'Called to create a new thread worker thread. 'CLSID can be obtained from a ProgID via CLSIDFromProgID 'InputData contains the data for the new thread. This ' should never be an object reference. 'fKeepOutputData should be True if you want to retrieve ' output data with GetWorkerOutput. This must be set for ' a valid cookie to be returned in OutputDataCookie. 'OutputDataCookie retrieves a cookie that can be used ' later to retrieve the exit code and output variant ' from a completed worker thread. 'fStealInputData should be True if the data is large. If ' this is set, then Data will be Empty on return. 'fReturnThreadHandle must explicitly be set to True to ' return the created thread handle. This handle can be ' used for calls like SetThreadPriority and must be ' closed with CloseHandle. Friend Function CreateWorkerThread(CLSID As VBoostTypes.CLSID, InputData As Variant, Optional ByVal fKeepOutputData As Boolean = False, Optional OutputDataCookie As Long, Optional ByVal fStealInputData As Boolean = False, Optional ByVal fReturnThreadHandle As Boolean = False) As Long Dim InitThreadData As ThreadData Dim ThreadID As Long Dim ThreadHandle As Long Dim hProcess As Long Dim pThreadData As Long Dim vt As Integer CleanCompletedThreads 'We need to clean up sometime, this is as good a time as any With InitThreadData .CLSID = CLSID .pCritSect = m_pCS Set .Controller = Me If fStealInputData Then 'Disallow stealing VT_BYREF data VBoostTypes.CopyMemory vt, InputData, 2 If vt And &H4000 Then GoTo DontSteal #If NOVBOOST Then VBoostTypes.CopyMemory ByVal VarPtr(.InputData), ByVal VarPtr(InputData), 16 VBoostTypes.CopyMemory ByVal VarPtr(InputData), 0, 2 #Else VBoost.MoveVariant .InputData, InputData #End If 'ElseIf IsObject(Data) Then 'Don't support this case, no objects allowed in data Else DontSteal: .InputData = InputData End If .fKeepData = fKeepOutputData End With pThreadData = modThreadData.NewThreadData(InitThreadData) m_RunningThreads.Add pThreadData, CStr(pThreadData) If fKeepOutputData Then OutputDataCookie = pThreadData End If ThreadHandle = ThreadingAPIs.CreateThread(0, 0, AddressOf ThreadProc.ThreadStart, pThreadData, 0, ThreadID) If ThreadHandle Then 'Turn ownership of the thread handle over to 'the ThreadData object modThreadData.ThreadHandle(pThreadData) = ThreadHandle If fReturnThreadHandle Then hProcess = ThreadingAPIs.GetCurrentProcess ThreadingAPIs.DuplicateHandle hProcess, ThreadHandle, hProcess, CreateWorkerThread End If End If End Function 'Call StopWorkerThreads to signal all worker threads 'and spin until they terminate. Friend Sub StopWorkerThreads() modThreadData.StopThreads m_RunningThreads, m_FinishedThreads, m_pCS End Sub 'Releases ThreadData objects for all threads 'that are completed. Cleaning happens automatically 'when you call SignalWorkerThreads, StopWorkerThreads, 'and GetWorkerOutput. Friend Sub CleanCompletedThreads(Optional ByVal fTossCompletedData As Boolean = False) Dim Iter As Variant modThreadData.CleanThreads m_RunningThreads, m_FinishedThreads If fTossCompletedData Then With m_FinishedThreads Do While .Count modThreadData.DestroyThreadData .Item(1) .Remove 1 Loop End With End If End Sub 'Call to tell all running worker threads to 'terminate. If the thread hasn't set its 'ThreadSignalPtr yet, then it can't be signaled 'Unlike StopWorkerThreads, this does not block 'while the workers actually terminate. 'SignalWorkerThreads must be called by the owner 'of this class before the ThreadControl instance 'is released. Friend Sub SignalWorkerThreads() modThreadData.SignalThreads m_RunningThreads, m_FinishedThreads, m_pCS End Sub 'Call to retrieve the data and exit code from 'a worker thread launched with CreateWorkerThread. 'This will return False if the thread has not 'yet completed. You get one call to GetWorkerOutput 'for each cookie. Friend Function GetWorkerOutput(ByVal OutputDataCookie As Long, hr As Long, ExitCode As Long, OutputData As Variant) As Boolean Dim DataKey As String CleanCompletedThreads DataKey = CStr(OutputDataCookie) On Error Resume Next m_FinishedThreads.Item DataKey If Err Then On Error GoTo 0 Exit Function End If On Error GoTo 0 modThreadData.GetOutputData OutputDataCookie, hr, ExitCode, OutputData modThreadData.DestroyThreadData OutputDataCookie m_FinishedThreads.Remove DataKey GetWorkerOutput = True End Function Private Sub Class_Initialize() Set m_RunningThreads = New Collection Set m_FinishedThreads = New Collection m_pCS = VarPtr(m_CS) ThreadingAPIs.InitializeCriticalSection m_pCS End Sub Private Sub Class_Terminate() CleanCompletedThreads True 'Just in case, this generally only cleans completed data. If m_pCS Then ThreadingAPIs.DeleteCriticalSection m_pCS End Sub