Option Explicit
#If VBA7 Then
#If Win64 Then
Private Declare PtrSafe Function GetTickCount Lib "kernel32" Alias "GetTickCount64" () As Long
#Else
Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As Long
#End If
Private Declare PtrSafe Function SetTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
Private Declare PtrSafe Function KillTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
Private Declare PtrSafe Function PlaySound Lib "winmm.dll" Alias "sndPlaySoundW" (ByVal lpszSoundName As LongPtr, Optional ByVal uFlags As Long) As Long
Private Declare PtrSafe Function AddAtom Lib "kernel32" Alias "AddAtomA" (ByVal lpString As String) As Integer
Private Declare PtrSafe Function GetAtomName Lib "kernel32" Alias "GetAtomNameA" (ByVal nAtom As Integer, ByVal lpBuffer As String, ByVal nSize As Long) As Long
Private Declare PtrSafe Function DeleteAtom Lib "kernel32" (ByVal nAtom As Integer) As Integer
#Else
Private Enum LongPtr
[_]
End Enum
Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Declare Function SetTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr, ByVal uElapse As Long, ByVal lpTimerFunc As LongPtr) As LongPtr
Private Declare Function KillTimer Lib "user32" (ByVal hwnd As LongPtr, ByVal nIDEvent As LongPtr) As Long
Private Declare Function PlaySound Lib "winmm.dll" Alias "sndPlaySoundW" (ByVal lpszSoundName As LongPtr, Optional ByVal uFlags As Long) As Long
Private Declare Function AddAtom Lib "kernel32" Alias "AddAtomA" (ByVal lpString As String) As Integer
Private Declare Function GetAtomName Lib "kernel32" Alias "GetAtomNameA" (ByVal nAtom As Integer, ByVal lpBuffer As String, ByVal nSize As Long) As Long
Private Declare Function DeleteAtom Lib "kernel32" (ByVal nAtom As Integer) As Integer
#End If
Public bTimerOn As Boolean, bAbort As Boolean
Public Function RunCountDown( _
ByVal Shp As Shape, _
ByVal TimerDurationInSeconds As Long, _
Optional ByVal PlayWavByWhenInSeconds As Long, _
Optional ByVal WawFilePathName As String _
) As Boolean
Dim lStartTime As Long, sAtomName As String
If bTimerOn Then MsgBox "Countdown already running.": Exit Function
If TimerDurationInSeconds <= 0 Then MsgBox "TimerDurationInSeconds must be >0 Secs": Exit Function
lStartTime = GetTickCount + (TimerDurationInSeconds * 1000&)
sAtomName = lStartTime & "|" & Shp.Name & "|" & TimerDurationInSeconds & "|" & _
PlayWavByWhenInSeconds & "|" & WawFilePathName & "|" & Shp.Parent.Name
Shp.TextFrame2.TextRange.Text = Format((DateAdd("s", TimerDurationInSeconds, Now()) - Now()), "nn:ss")
Call CountDown(nIDEvent:=AddAtom(sAtomName))
Call SetTimer(Application.hwnd, AddAtom(sAtomName), 1000&, AddressOf CountDown)
RunCountDown = True
End Function
Private Sub CountDown( _
Optional ByVal hwnd As LongPtr, _
Optional ByVal uMsg As Long, _
Optional ByVal nIDEvent As LongPtr, _
Optional ByVal dwTimer As Long _
)
Dim lStartTime As Long
Dim sShapeName As String
Dim lTimerDuration As Long
Dim lPlayWavBy As Long
Dim sWawFilePathName As String
Dim oParentSheet As Worksheet
Dim sBuffer As String * 256&, lRet As Long
Dim sAtomName As String, sAtomNameParts() As String
Dim dtSecsPassed As Date
On Error Resume Next
bTimerOn = True
lRet = GetAtomName(CInt(nIDEvent), sBuffer, Len(sBuffer))
sAtomName = Left(sBuffer, lRet)
If Len(sAtomName) Then
sAtomNameParts = Split(sAtomName, "|")
lStartTime = CLng(sAtomNameParts(0&))
sShapeName = sAtomNameParts(1&)
lTimerDuration = CLng(sAtomNameParts(2&))
lPlayWavBy = CLng(sAtomNameParts(3&))
sWawFilePathName = sAtomNameParts(4&)
Set oParentSheet = Sheets(sAtomNameParts(5&))
End If
dtSecsPassed = ((lStartTime - dwTimer) / 1000&) / 24& / 60& / 60&
If Len(Dir(sWawFilePathName)) Then
If (DateDiff("s", 0&, dtSecsPassed) <= lPlayWavBy) Or (uMsg = 0& And (lPlayWavBy >= lTimerDuration)) Then
Call PlayWav(sWawFilePathName)
End If
End If
With oParentSheet.Shapes(sShapeName)
If uMsg <> 0& Then
.TextFrame2.TextRange.Text = Format(((dtSecsPassed)), "nn:ss")
End If
If DateDiff("s", 0&, dtSecsPassed) = 0& Or bAbort Then
.TextFrame2.TextRange.Text = Format(0&, "nn:ss")
Call DeleteAtom(CInt(nIDEvent))
Call StopTimer(nIDEvent)
End If
End With
End Sub
Private Sub StopTimer(ByVal TimerId As LongPtr)
Call KillTimer(Application.hwnd, TimerId)
Application.OnTime Now + TimeSerial(0&, 0&, 1&), "StopWav"
bTimerOn = False: bAbort = False
Debug.Print "Done!"
End Sub
Private Sub PlayWav(ByVal WavFilePathName As String)
Const SND_FILENAME = &H20000, SND_ASYNC = &H1, SND_LOOP = &H8
Call PlaySound(StrPtr(WavFilePathName), SND_ASYNC + SND_FILENAME + SND_LOOP)
End Sub
Private Sub StopWav()
Call PlaySound(StrPtr(vbNullString))
End Sub