I have no problem using timers. In fact, I think this is a good candidate for using a timer...
TimerWatchCalc.zip
In a userform named "CalcLauncher":
Controls: Label3 , Label4, CommandButton1<table width="100%" border="1" bgcolor="White" style="filter
rogid:DXImageTransform.Microsoft.Gradient(endColorstr='#C0CFE2', startColorstr='#FFFFFF', gradientType='0');"><tr><TD><font size="2" face=Courier New> <font color="#0000A0">Option</font> <font color="#0000A0">Explicit</font>
<font color="#0000A0">Private</font> <font color="#0000A0">Sub</font> CommandButton1_Click()
<font color="#0000A0">Call</font> LaunchAndWatchCalculator(Me)
<font color="#0000A0">End</font> <font color="#0000A0">Sub</font>
<font color="#0000A0">Friend</font> <font color="#0000A0">Sub</font> CalcEditChange(EditText <font color="#0000A0">As</font> Double)
Label3 = EditText
<font color="#0000A0">End</font> <font color="#0000A0">Sub</font>
<font color="#0000A0">Friend</font> <font color="#0000A0">Sub</font> CalcClosed(Result <font color="#0000A0">As</font> Double)
Label4 = Result
Me.Repaint
<font color="#0000A0">End</font> <font color="#0000A0">Sub</font>
<font color="#0000A0">Private</font> <font color="#0000A0">Sub</font> UserForm_Terminate()
<font color="#0000A0">Call</font> StopWatching
<font color="#0000A0">End</font> <font color="#0000A0">Sub</font>
</FONT></td></tr></table><button onclick='document.all("98200620950437").value=document.all("98200620950437").value.replace(/<br \/>\s\s/g,"");document.all("98200620950437").value=document.all("98200620950437").value.replace(/<br \/>/g,"");window.clipboardData.setData("Text",document.all("98200620950437").value);'>Copy to Clipboard</BUTTON><textarea style="position:absolute;visibility:hidden" name="98200620950437" wrap="virtual">
Option Explicit
Private Sub CommandButton1_Click()
Call LaunchAndWatchCalculator(Me)
End Sub
Friend Sub CalcEditChange(EditText As Double)
Label3 = EditText
End Sub
Friend Sub CalcClosed(Result As Double)
Label4 = Result
Me.Repaint
End Sub
Private Sub UserForm_Terminate()
Call StopWatching
End Sub</textarea>
In a standard module:
<table width="100%" border="1" bgcolor="White" style="filter
rogid:DXImageTransform.Microsoft.Gradient(endColorstr='#C0CFE2', startColorstr='#FFFFFF', gradientType='0');"><tr><TD><font size="2" face=Courier New> <font color="#0000A0">Option</font> <font color="#0000A0">Explicit</font>
<font color="#0000A0">Private</font> <font color="#0000A0">Declare</font> <font color="#0000A0">Function</font> SetTimer <font color="#0000A0">Lib</font> "user32" (ByVal hwnd <font color="#0000A0">As</font> Long, <font color="#0000A0">ByVal</font> nIDEvent <font color="#0000A0">As</font> Long, <font color="#0000A0">ByVal</font> uElapse <font color="#0000A0">As</font> Long, <font color="#0000A0">ByVal</font> lpTimerFunc <font color="#0000A0">As</font> Long) <font color="#0000A0">As</font> <font color="#0000A0">Long</font>
<font color="#0000A0">Private</font> <font color="#0000A0">Declare</font> <font color="#0000A0">Function</font> KillTimer <font color="#0000A0">Lib</font> "user32" (ByVal hwnd <font color="#0000A0">As</font> Long, <font color="#0000A0">ByVal</font> nIDEvent <font color="#0000A0">As</font> Long) <font color="#0000A0">As</font> <font color="#0000A0">Long</font>
<font color="#0000A0">Private</font> <font color="#0000A0">Declare</font> <font color="#0000A0">Function</font> IsWindow <font color="#0000A0">Lib</font> "user32" (ByVal hwnd <font color="#0000A0">As</font> Long) <font color="#0000A0">As</font> <font color="#0000A0">Long</font>
<font color="#0000A0">Private</font> <font color="#0000A0">Declare</font> <font color="#0000A0">Function</font> GetForegroundWindow <font color="#0000A0">Lib</font> "user32" () <font color="#0000A0">As</font> <font color="#0000A0">Long</font>
<font color="#0000A0">Private</font> <font color="#0000A0">Declare</font> <font color="#0000A0">Function</font> FindWindowEx <font color="#0000A0">Lib</font> "user32" <font color="#0000A0">Alias</font> "FindWindowExA" (ByVal hWnd1 <font color="#0000A0">As</font> Long, <font color="#0000A0">ByVal</font> hWnd2 <font color="#0000A0">As</font> Long, <font color="#0000A0">ByVal</font> lpsz1 <font color="#0000A0">As</font> String, <font color="#0000A0">ByVal</font> lpsz2 <font color="#0000A0">As</font> String) <font color="#0000A0">As</font> <font color="#0000A0">Long</font>
<font color="#0000A0">Private</font> <font color="#0000A0">Declare</font> <font color="#0000A0">Function</font> SendMessage <font color="#0000A0">Lib</font> "user32" <font color="#0000A0">Alias</font> "SendMessageA" (ByVal hwnd <font color="#0000A0">As</font> Long, <font color="#0000A0">ByVal</font> wMsg <font color="#0000A0">As</font> Long, <font color="#0000A0">ByVal</font> wParam <font color="#0000A0">As</font> Long, lParam <font color="#0000A0">As</font> Any) <font color="#0000A0">As</font> <font color="#0000A0">Long</font>
<font color="#0000A0">Private</font> <font color="#0000A0">Declare</font> <font color="#0000A0">Function</font> FindWindow <font color="#0000A0">Lib</font> "user32" <font color="#0000A0">Alias</font> "FindWindowA" (ByVal lpClassName <font color="#0000A0">As</font> String, <font color="#0000A0">ByVal</font> lpWindowName <font color="#0000A0">As</font> String) <font color="#0000A0">As</font> <font color="#0000A0">Long</font>
<font color="#0000A0">Private</font> <font color="#0000A0">Declare</font> <font color="#0000A0">Function</font> SetWindowText <font color="#0000A0">Lib</font> "user32" <font color="#0000A0">Alias</font> "SetWindowTextA" (ByVal hwnd <font color="#0000A0">As</font> Long, <font color="#0000A0">ByVal</font> lpString <font color="#0000A0">As</font> String) <font color="#0000A0">As</font> <font color="#0000A0">Long</font>
<font color="#0000A0">Private</font> <font color="#0000A0">Const</font> WM_GETTEXT = &HD
<font color="#0000A0">Private</font> <font color="#0000A0">Const</font> WM_GETTEXTLENGTH = &HE
<font color="#0000A0">Private</font> <font color="#0000A0">Const</font> CalcCaption <font color="#0000A0">As</font> <font color="#0000A0">String</font> = "Joe Was Here..."
<font color="#0000A0">Private</font> TimerID <font color="#0000A0">As</font> <font color="#0000A0">Long</font>
<font color="#0000A0">Private</font> OldValue <font color="#0000A0">As</font> <font color="#0000A0">Double</font>
<font color="#0000A0">Private</font> CallBackParent <font color="#0000A0">As</font> CalcLauncher
<font color="#0000A0">Private</font> CalcHwnd <font color="#0000A0">As</font> <font color="#0000A0">Long</font>
<font color="#0000A0">Private</font> EditHwnd <font color="#0000A0">As</font> <font color="#0000A0">Long</font>
<font color="#0000A0">Sub</font> LaunchAndWatchCalculator(cl <font color="#0000A0">As</font> CalcLauncher, <font color="#0000A0">Optional</font> PollingInterval <font color="#0000A0">As</font> <font color="#0000A0">Long</font> = 100)
Shell "calc"
CalcHwnd = GetForegroundWindow
SetWindowText CalcHwnd, CalcCaption
EditHwnd = FindWindowEx(CalcHwnd, 0, "Edit", vbNullString)
<font color="#0000A0">Set</font> CallBackParent = cl
OldValue = -1
TimerID = SetTimer(0&, 0&, PollingInterval, <font color="#0000A0">AddressOf</font> TimerProc)
<font color="#0000A0">End</font> <font color="#0000A0">Sub</font>
<font color="#0000A0">Sub</font> StopWatching()
KillTimer 0&, TimerID
<font color="#0000A0">Set</font> CallBackParent = <font color="#0000A0">Nothing</font>
<font color="#0000A0">End</font> <font color="#0000A0">Sub</font>
<font color="#0000A0">Private</font> <font color="#0000A0">Sub</font> TimerProc(ByVal hwnd <font color="#0000A0">As</font> Long, <font color="#0000A0">ByVal</font> uMsg <font color="#0000A0">As</font> Long, _
<font color="#0000A0">ByVal</font> nIDEvent <font color="#0000A0">As</font> Long, <font color="#0000A0">ByVal</font> dwTimer <font color="#0000A0">As</font> Long)
<font color="#0000A0">On</font> <font color="#0000A0">Error</font> <font color="#0000A0">Resume</font> <font color="#0000A0">Next</font>
<font color="#0000A0">If</font> IsWindow(CalcHwnd) = 0 <font color="#0000A0">Or</font> FindWindow(vbNullString, CalcCaption) = 0 <font color="#0000A0">Then</font>
<font color="#0000A0">Call</font> CallBackParent.CalcClosed(OldValue)
<font color="#0000A0">Call</font> StopWatching
<font color="#0000A0">Exit</font> <font color="#0000A0">Sub</font>
<font color="#0000A0">End</font> <font color="#0000A0">If</font>
<font color="#0000A0">Dim</font> EditText <font color="#0000A0">As</font> String, ToDouble <font color="#0000A0">As</font> <font color="#0000A0">Double</font>
EditText = Space(SendMessage(EditHwnd, WM_GETTEXTLENGTH, <font color="#0000A0">ByVal</font> 0, <font color="#0000A0">ByVal</font> 0))
SendMessage EditHwnd, WM_GETTEXT, <font color="#0000A0">ByVal</font> Len(EditText) + 1, <font color="#0000A0">ByVal</font> EditText
<font color="#0000A0">If</font> IsNumeric(EditText) <font color="#0000A0">Then</font>
ToDouble = CDbl(EditText)
<font color="#0000A0">If</font> OldValue <> ToDouble <font color="#0000A0">Then</font>
<font color="#008000"> 'add a comment if you do not want a pseudo change event</font>
<font color="#0000A0">Call</font> CallBackParent.CalcEditChange(ToDouble)
OldValue = ToDouble
<font color="#0000A0">End</font> <font color="#0000A0">If</font>
<font color="#0000A0">End</font> <font color="#0000A0">If</font>
<font color="#0000A0">End</font> <font color="#0000A0">Sub</font>
</FONT></td></tr></table><button onclick='document.all("982006212154500").value=document.all("982006212154500").value.replace(/<br \/>\s\s/g,"");document.all("982006212154500").value=document.all("982006212154500").value.replace(/<br \/>/g,"");window.clipboardData.setData("Text",document.all("982006212154500").value);'>Copy to Clipboard</BUTTON><textarea style="position:absolute;visibility:hidden" name="982006212154500" wrap="virtual">
Option Explicit
Private Declare Function SetTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib "user32" (ByVal hwnd As Long, ByVal nIDEvent As Long) As Long
Private Declare Function IsWindow Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetForegroundWindow Lib "user32" () As Long
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function SetWindowText Lib "user32" Alias "SetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String) As Long
Private Const WM_GETTEXT = &HD
Private Const WM_GETTEXTLENGTH = &HE
Private Const CalcCaption As String = "Joe Was Here..."
Private TimerID As Long
Private OldValue As Double
Private CallBackParent As CalcLauncher
Private CalcHwnd As Long
Private EditHwnd As Long
Sub LaunchAndWatchCalculator(cl As CalcLauncher, Optional PollingInterval As Long = 100)
Shell "calc"
CalcHwnd = GetForegroundWindow
SetWindowText CalcHwnd, CalcCaption
EditHwnd = FindWindowEx(CalcHwnd, 0, "Edit", vbNullString)
Set CallBackParent = cl
OldValue = -1
TimerID = SetTimer(0&, 0&, PollingInterval, AddressOf TimerProc)
End Sub
Sub StopWatching()
KillTimer 0&, TimerID
Set CallBackParent = Nothing
End Sub
Private Sub TimerProc(ByVal hwnd As Long, ByVal uMsg As Long, _
ByVal nIDEvent As Long, ByVal dwTimer As Long)
On Error Resume Next
If IsWindow(CalcHwnd) = 0 Or FindWindow(vbNullString, CalcCaption) = 0 Then
Call CallBackParent.CalcClosed(OldValue)
Call StopWatching
Exit Sub
End If
Dim EditText As String, ToDouble As Double
EditText = Space(SendMessage(EditHwnd, WM_GETTEXTLENGTH, ByVal 0, ByVal 0))
SendMessage EditHwnd, WM_GETTEXT, ByVal Len(EditText) + 1, ByVal EditText
If IsNumeric(EditText) Then
ToDouble = CDbl(EditText)
If OldValue <> ToDouble Then
'add a comment if you do not want a pseudo change event
Call CallBackParent.CalcEditChange(ToDouble)
OldValue = ToDouble
End If
End If
End Sub</textarea>
TimerWatchCalc.zip