FrmMain.frm
VERSION 5.00
Begin VB.Form frmMain
BorderStyle = 1 'Fixed Single
Caption = "AddressOf Example - Enumerate Windows"
ClientHeight = 4695
ClientLeft = 45
ClientTop = 330
ClientWidth = 5160
LinkTopic = "Form1"
MaxButton = 0 'False
ScaleHeight = 4695
ScaleWidth = 5160
StartUpPosition = 2 'CenterScreen
Begin VB.CommandButton btnEnd
Caption = "E&nd"
Height = 495
Left = 2640
TabIndex = 2
Top = 3960
Width = 2295
End
Begin VB.CommandButton btnEnumThem
Caption = "&Enumerate Windows"
Height = 495
Left = 120
TabIndex = 1
Top = 3960
Width = 2415
End
Begin VB.ListBox lstOutput
BeginProperty Font
Name = "Courier New"
Size = 9.75
Charset = 0
Weight = 400
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 3420
Left = 120
TabIndex = 0
Top = 120
Width = 4815
End
End
Attribute VB_Name = "frmMain"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' John Robbins, Microsoft Systems Journal - August 1997
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Option Explicit
Private Sub btnEnd_Click()
End
End Sub
Private Sub btnEnumThem_Click()
lstOutput.Clear
Dim bRet As Long
bRet = EnumWindows(AddressOf WndEnumProc, lstOutput)
End Sub
EnumWnds.bas
Attribute VB_Name = "mod_EnumWnds"
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' John Robbins, Microsoft Systems Journal - August 1997
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Option Explicit
' Declare the API functions.
Declare Function EnumWindows Lib "User32" _
(ByVal lpEnumFunc As Any, ByVal lParam As Any) As Long
Declare Function GetWindowText Lib "User32" _
Alias "GetWindowTextA" _
(ByVal hwnd As Long, _
ByVal lpString As String, _
ByVal cch As Long) As Long
Function WndEnumProc(ByVal hwnd As Long, _
ByVal lParam As ListBox) As Long
Dim szTitle As String
Dim bRet As Long
szTitle = String(512, 0)
bRet = GetWindowText(hwnd, szTitle, 512)
' Only show those that have titles.
If (0 <> bRet) Then
lParam.AddItem szTitle
End If
WndEnumProc = 1
End Function
Figure 2 MT.bas
Attribute VB_Name = "MultiThreaded_Main"
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' John Robbins, Microsoft Systems Journal - August 1997
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE **
' NEVER RUN THIS PROGRAM IN THE VB IDE!
' ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE ** NOTE **
Option Explicit
' Note: This declaration of CreateThread does not allow you to pass
' thread attributes. This is the CreateThread that should be used most
' often because the parameter for the thread function is passed by
' reference.
Public Declare Function CreateThread Lib "kernel32" _
(ByVal lpThreadAttributes As Long, _
ByVal dwStackSize As Long, _
ByVal lpStartAddress As Any, _
ByRef lpParameter As Any, _
ByVal dwCreationFlags As Long, _
ByRef lpThreadId As Long) As Long
' If you only are passing a single variable type like an integer or
' string as the thread parameter, use this version of CreateThread.
' Note that I set the lpParameter type to long.
Public Declare Function CreateThread_ByValParam Lib "kernel32" _
Alias "CreateThread" _
(ByVal lpThreadAttributes As Long, _
ByVal dwStackSize As Long, _
ByVal lpStartAddress As Any, _
ByVal lpParameter As Long, _
ByVal dwCreationFlags As Long, _
ByRef lpThreadId As Long) As Long
' So you can determine which thread the code is executing in.
Public Declare Function GetCurrentThreadId Lib "kernel32" () As Long
' This is the type that is passed to DaThreadFunc so you can do the by
' reference demonstration.
Type PARAM_TYPE
lValue As Long
End Type
' The function used to show a pass by reference.
Function DaThreadFunc(ByRef lpParam As PARAM_TYPE) As Long
Dim szStr As String
szStr = "From DaThreadFunc - Parameter = " & _
CStr(lpParam.lValue) & vbNewLine & _
"Thread ID: " & CStr(GetCurrentThreadId)
MsgBox szStr, , "Function Cool!"
' Just to make the return value different, I return -2.
DaThreadFunc = -2
End Function
' The subroutine to show pass by value.
' By the way, I noticed that subs always return 0.
Sub DaThreadSub(ByVal lpVoid As Long)
Dim szStr As String
szStr = "From DaThreadSub - Parameter = " & _
CStr(lpVoid) & vbNewLine & _
"Thread ID: " & CStr(GetCurrentThreadId)
MsgBox szStr, , "Sub Cool!"
End Sub
Sub Main()
Dim lRet As Long
Dim lThreadID As Long
Dim stParam As PARAM_TYPE
Dim szStr As String
lThreadID = 0
' Do the call to CreateThread with a by reference parameter.
stParam.lValue = -1
lRet = CreateThread(0, _
0, _
AddressOf DaThreadFunc, _
stParam, _
0, _
lThreadID)
' Do the call to CreateThread with a by value parameter.
lRet = CreateThread_ByValParam(0, _
0, _
AddressOf DaThreadSub, _
-2, _
0, _
lThreadID)
szStr = "From Da Main Thread" & vbNewLine & _
"Thread ID: " & CStr(GetCurrentThreadId)
MsgBox szStr
End Sub
Figure 4 Event Solution Sample
frmMain.frm
VERSION 5.00
Begin VB.Form fmrMain
Caption = "Form1"
ClientHeight = 1740
ClientLeft = 60
ClientTop = 345
ClientWidth = 4680
LinkTopic = "Form1"
ScaleHeight = 1740
ScaleWidth = 4680
StartUpPosition = 3 'Windows Default
Begin VB.CommandButton btnNoWith
Caption = "Using the output class to fake events!"
Height = 855
Left = 600
TabIndex = 0
Top = 360
Width = 3375
End
End
Attribute VB_Name = "fmrMain"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' John Robbins
' Microsoft Systems Journal - August, 1997
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Option Explicit
Private g_ClassNoWith As TheClass
Private g_Output As OutputClass
Private Sub Form_Load()
Set g_ClassNoWith = New TheClass
Set g_Output = New OutputClass
' Set the output class property of the "NoWith" class.
Set g_ClassNoWith.clsOutput = g_Output
End Sub
Private Sub btnNoWith_Click()
Dim hThread As Long
Dim lThreadID As Long
' Create the thread.
hThread = CreateThread(0, _
0, _
AddressOf TheThread, _
g_ClassNoWith, _
0, _
lThreadID)
End Sub
OutputClass.cls
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "OutputClass"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' John Robbins, Microsoft Systems Journal - August, 1997
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Option Explicit
Public Sub DoMe(msg As String)
Dim iRet As Integer
iRet = MsgBox(msg, Title:="OutputClass")
End Sub
TheClass.cls
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "TheClass"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' John Robbins, Microsoft Systems Journal - August, 1997
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Option Explicit
Public clsOutput As OutputClass
' The public method that will be called from the thread.
Public Sub CallMeFromTheThread()
Dim iRet As Integer
iRet = MsgBox("TheClass.CallMeFromTheThread did something", _
Title:="TheClass")
End Sub
Thread.bas
Attribute VB_Name = "Thread"
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' John Robbins, Microsoft Systems Journal - August, 1997
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Option Explicit
Public Declare Function CreateThread Lib "kernel32" _
(ByVal lpThreadAttributes As Long, _
ByVal dwStackSize As Long, _
ByVal lpStartAddress As Any, _
ByRef lpParameter As Any, _
ByVal dwCreationFlags As Long, _
ByRef lpThreadId As Long) As Long
Public Function TheThread(clsTheClass As TheClass) As Long
On Error GoTo TheThread_Error
clsTheClass.CallMeFromTheThread
clsTheClass.clsOutput.DoMe ("I'm from the thread!")
Exit Function
TheThread_Error:
MsgBox ("TheThread had an error : " + Err.Description)
End Function
Figure 5 Thread Test Sample
frmThreadTest.frm
VERSION 5.00
Begin VB.Form frmThreadTest
BorderStyle = 1 'Fixed Single
Caption = "Thread Tester!"
ClientHeight = 1725
ClientLeft = 45
ClientTop = 330
ClientWidth = 5805
LinkTopic = "Form1"
MaxButton = 0 'False
ScaleHeight = 1725
ScaleWidth = 5805
StartUpPosition = 2 'CenterScreen
Begin VB.CommandButton btnPause
Caption = "&Pause Thread"
Height = 615
Left = 1533
TabIndex = 3
Top = 960
Width = 1335
End
Begin VB.CommandButton btnStartThread
Caption = "&Start Thread"
Height = 615
Left = 131
TabIndex = 2
Top = 960
Width = 1335
End
Begin VB.CommandButton btnStopThread
Caption = "Stop &Thread"
Height = 615
Left = 2935
TabIndex = 4
Top = 960
Width = 1335
End
Begin VB.CommandButton btnEnd
Caption = "&End"
Height = 615
Left = 4338
TabIndex = 5
Top = 960
Width = 1335
End
Begin VB.Label lblSeconds
Caption = "0.0"
BeginProperty Font
Name = "MS Sans Serif"
Size = 24
Charset = 0
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
ForeColor = &H000000FF&
Height = 495
Left = 4200
TabIndex = 1
Top = 240
Width = 1335
End
Begin VB.Label lblTitle
Caption = "Look Ma! No timers, just real threads!"
BeginProperty Font
Name = "MS Sans Serif"
Size = 9.75
Charset = 0
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 495
Left = 120
TabIndex = 0
Top = 240
Width = 3855
End
End
Attribute VB_Name = "frmThreadTest"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' John Robbins, Microsoft Systems Journal - August 1997
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Option Explicit
' This is the static thread handle that will be used by this form
' to store the executing thread. If it is 0, then the background
' thread is not executing.
Private g_hThread As Long
' The array of event handles that are used to coordinate the background
' thread. There are two events here. The first is the stop event
' that when signaled means that the background thread is supposed
' to end. The second event is the event that is signaled when the
' background thread is supposed to be updating the label control
' with the count. If it is nonsignaled, then the background thread
' is paused. The second event is the paused/running event.
Private g_ahSynchEvents(2) As Long
' The flag that indicates the state of the thread so the UI can show the
' state.
Private g_bThreadIsPaused As Boolean
' The class that holds the label object that will be passed to the
' thread. This is done because trying to pass the actual label
' as the lpParameter to CreateThread always passed zero which obviously
' will crash.
Private g_clsLabel As LabelClass
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Handles when the form is loaded.
Private Sub Form_Load()
' Set the statics to known values, I trust nothing.
g_hThread = 0
g_ahSynchEvents(0) = 0
g_ahSynchEvents(1) = 0
g_bThreadIsPaused = False
' Instantiate the class.
Set g_clsLabel = New LabelClass
' Initialize the label class so that you can really pass the label
' to the thread as the parameter.
Set g_clsLabel.m_lblLabel = lblSeconds
' Disable the stop thread and pause buttons.
btnStopThread.Enabled = False
btnPause.Enabled = False
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Called when the form unloads. If the background thread is running,
' then it is killed.
Private Sub Form_Unload(Cancel As Integer)
' Shut down the thread if needed.
KillTheThread
Set g_clsLabel.m_lblLabel = Nothing
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' The "End" button is pressed.
Private Sub btnEnd_Click()
Unload frmThreadTest
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' The user wants to start the thread.
Private Sub btnStartThread_Click()
' Make sure that you only get in here if no thread is executing.
#If DEBUGBUILD Then
If (0 <> g_hThread) Then
MsgBox ("btnStartThread_Click: g_hThread <> 0")
End If
#End If
' Create the synchronization objects now.
CreateSynchEvents g_ahSynchEvents
' Holds the handle id returned by CreateThread.
Dim lThreadID As Long
' Create the thread.
g_hThread = CreateThread(0, _
0, _
AddressOf TheThread.TheThreadSub, _
g_clsLabel, _
0, lThreadID)
#If DEBUGBUILD Then
If (0 = g_hThread) Then
MsgBox ("btnStartThread_Click: g_hThread <> 0")
End If
#End If
If (0 <> g_hThread) Then
' Disable the start thread button so you don't create extra
' threads.
btnStartThread.Enabled = False
' Enable the end thread button so you can kill it.
btnStopThread.Enabled = True
' Enable the pause button.
btnPause.Enabled = True
Else
' We didn't get started so kill the synch objects.
Dim bRet As Long
bRet = CloseHandle(g_ahSynchEvents(0))
#If DEBUGBUILD Then
If (0 = bRet) Then
MsgBox ("CloseHandle(g_ahSynchEvents(0)) failed")
End If
#End If
bRet = CloseHandle(g_ahSynchEvents(1))
#If DEBUGBUILD Then
If (0 = bRet) Then
MsgBox ("CloseHandle(g_ahSynchEvents(1)) failed")
End If
#End If
End If
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' The user wants to pause the thread.
Private Sub btnPause_Click()
#If DEBUGBUILD Then
If (0 = g_hThread) Then
MsgBox ("btnStartThread_Click: g_hThread = 0")
End If
#End If
Dim bRet As Long
' Is the thread already paused? If it is, then you need to set the
' update event, the second in the array, and put the "Pause Thread"
' text back on the button.
If (True = g_bThreadIsPaused) Then
' Reset the flag.
g_bThreadIsPaused = False
' Set the text back.
btnPause.Caption = "&Pause Thread"
' Set the event to signaled.
bRet = SetEvent(g_ahSynchEvents(1))
#If DEBUGBUILD Then
If (0 = bRet) Then
MsgBox ("SetEvent(g_ahSynchEvents(1)) failed")
End If
#End If
Else
' Set the flag.
g_bThreadIsPaused = True
' Set the button caption.
btnPause.Caption = "&Resume Thread"
' Set the paused/running event to nonsignaled.
bRet = ResetEvent(g_ahSynchEvents(1))
#If DEBUGBUILD Then
If (0 = bRet) Then
MsgBox ("ResetEvent(g_ahSynchEvents(1)) failed")
End If
#End If
End If
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' The user wants to stop the thread.
Private Sub btnStopThread_Click()
#If DEBUGBUILD Then
If (0 = g_hThread) Then
MsgBox ("btnStopThread_Click: g_hThread = 0")
End If
#End If
KillTheThread
' If the thread is paused, then clear the flag and set the button
' back to the proper text.
If (True = g_bThreadIsPaused) Then
' Reset the flag.
g_bThreadIsPaused = False
' Set the text back.
btnPause.Caption = "&Pause Thread"
End If
' Reset the state of the buttons.
btnStartThread.Enabled = True
btnStopThread.Enabled = False
btnPause.Enabled = False
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' A private routine to end the background thread. This is here because
' it needs to be called from the "Stop" button and during the form
' unload.
Private Sub KillTheThread()
If (0 <> g_hThread) Then
' Just kill the thread!
Dim bRet As Long
' It is, so signal the quit event then shut down.
bRet = SetEvent(g_ahSynchEvents(0))
#If DEBUGBUILD Then
If (0 = bRet) Then
MsgBox ("SetEvent(g_ahSynchEvents(0)) failed")
End If
#End If
' Wait for the thread to end.
bRet = WaitForSingleObject(g_hThread, INFINITE)
' Close down the synchronization handles.
bRet = CloseHandle(g_ahSynchEvents(0))
#If DEBUGBUILD Then
If (0 = bRet) Then
MsgBox ("CloseHandle(g_ahSynchEvents(0)) failed")
End If
#End If
bRet = CloseHandle(g_ahSynchEvents(1))
#If DEBUGBUILD Then
If (0 = bRet) Then
MsgBox ("CloseHandle(g_ahSynchEvents(1)) failed")
End If
#End If
g_hThread = 0
End If
End Sub
Globals.bas
Attribute VB_Name = "Globals"
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' John Robbins, Microsoft Systems Journal - August 1997
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Option Explicit
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' The name of the synchronization object that, when signaled, is used to
' update the label.
Public Const SYNCHEVENT_UPDATELABEL As String = "ThreadTest_UpdateLabel"
' The name of the synchronization object that, when signaled, tells the
' background thread to end.
Public Const SYNCHEVENT_ENDTHREAD As String = "ThreadTest_EndThread"
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Declare Function CreateThread _
Lib "kernel32" (ByVal lpThreadAttributes As Long, _
ByVal dwStackSize As Long, _
ByVal lpStartAddress As Any, _
ByRef lpParameter As Any, _
ByVal dwCreationFlags As Long, _
ByRef lpThreadId As Long) As Long
Public Declare Function TerminateThread _
Lib "kernel32" (ByVal hThread As Long, _
ByVal dwExitCode As Long) As Long
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Declare Function CreateEvent _
Lib "kernel32" _
Alias "CreateEventA" _
(ByVal lpEventAttributes As Long, _
ByVal bManualReset As Long, _
ByVal bInitialState As Long, _
ByVal lpName As String) As Long
Declare Function CloseHandle _
Lib "kernel32" _
(ByVal hObject As Long) As Long
Declare Function WaitForMultipleObjects _
Lib "kernel32" (ByVal nCount As Long, _
ByRef lpHandles As Long, _
ByVal bWaitAll As Long, _
ByVal dwMilliseconds As Long) As Long
Declare Function WaitForSingleObject _
Lib "kernel32" (ByVal hHandle As Long, _
ByVal dwMilliseconds As Long) As Long
Declare Function SetEvent _
Lib "kernel32" (ByVal hEvent As Long) As Long
Declare Function ResetEvent _
Lib "kernel32" (ByVal hEvent As Long) As Long
Public Const INFINITE = &HFFFF
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' This is the helper function that will create the appropriate events
' for both the main thread and the background thread.
Sub CreateSynchEvents(ahEvents() As Long)
' The first event is the stop event so it is a manual-reset event
' that is created NONSIGNALED.
ahEvents(0) = CreateEvent(0, 1, 0, SYNCHEVENT_ENDTHREAD)
#If DEBUGBUILD Then
If (0 = ahEvents(0)) Then
MsgBox ("CreateSynchEvents: ahEvents(0) = 0")
End If
#End If
' The second event is the paused/running event. It is created as a
' manual-reset event that starts out as signaled.
ahEvents(1) = CreateEvent(0, 1, 1, SYNCHEVENT_UPDATELABEL)
#If DEBUGBUILD Then
If (0 = ahEvents(1)) Then
MsgBox ("CreateSynchEvents: ahEvents(1) = 0")
End If
#End If
End Sub
LabelClass.cls
VERSION 1.0 CLASS BEGIN MultiUse = -1 'True END Attribute VB_Name = "LabelClass" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = True Attribute VB_PredeclaredId = False Attribute VB_Exposed = False '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' John Robbins, Microsoft Systems Journal - August, 1997 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Option Explicit Public m_lblLabel As LabelTheThread.bas
Attribute VB_Name = "TheThread"
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' John Robbins, Microsoft Systems Journal - August 1997
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Option Explicit
' This is the procedure that will be used as the
' background thread.
Public Sub TheThreadSub(clsLabel As LabelClass)
' The array of event handles that are used to coordinate the
' background thread. There are two events here. The first is the
' stop event that when signaled means that the background thread is
' supposed to end. The second event is the event that is signaled
' when the background thread is supposed to be updating the label
' control with the count. If it is nonsignaled, then the
' background thread is paused. The second event is the
' paused/running event.
Dim ahSynchEvents(2) As Long
' Create the synchronization events.
CreateSynchEvents ahSynchEvents
' The number of seconds that we have been running.
Dim lSecs As Long
lSecs = 0
' All that is done in here is just spin around updating the label
' text until this thread gets killed. It's kind of boring,
' actually.
While True
Dim bRet As Long
Dim lIndex As Long
lIndex = WaitForMultipleObjects(2, ahSynchEvents(0), 0, INFINITE)
Select Case lIndex
' If it is the first one, then stop.
Case 0
' Clean up after ourselves.
bRet = CloseHandle(ahSynchEvents(0))
#If DEBUGBUILD Then
If (0 = bRet) Then
MsgBox ("CloseHandle(ahSynchEvents(0)) failed")
End If
#End If
bRet = CloseHandle(ahSynchEvents(1))
#If DEBUGBUILD Then
If (0 = bRet) Then
MsgBox ("CloseHandle(ahSynchEvents(1)) failed")
End If
#End If
Exit Sub
Case 1
' Show how long the thread has been running. This does not
' handle wrap around on the time.
clsLabel.m_lblLabel.Caption = lSecs / 100
' Sleep for a bit.
Sleep (100)
lSecs = lSecs + 1
Case Else
#If DEBUGBUILD Then
MsgBox ("Case Else hit!!")
#End If
End Select
Wend
End Sub