vba timer available in modules - vba

Could anyone please tell me if and how it is possible to count time of running various modules in VBA excel?
I am only able to count the elapsed time of one single module. However, when it comes to more than 1 module it seems there is no info out there. Or I am blind.
With one module I pass Timer value to a variable set at the begining of code and then at the end. Then I deduct second variable from the first.
When I try to do it between modules initial variable value is reset to empty.
Many thanks for any pointers
Tommeck37

For a simple way to measure the time it takes to execute portions of your code, you can use the VBA timer() function. That function isn't perfect, but it's a good place to start.
Timer() returns the number of seconds that have elapsed since midnight, including fractions.
Just before you call a module, assign the result of timer() to a variable. Timer returns a Double, but you could store it in a variant, too. Then do another call to timer() just after the module. For example:
time1 = timer()
call Proc1()
time2 = timer()
call Proc2()
time3 = timer()
debug.print "Proc1 time: " & cStr(time2-time1)
debug.print "Proc2 time: " & cStr(time3-time2)
There are other timers that you can use in VBA, but they require using windows procedures, and are harder to setup and use. For gross timing, this works fine. If you want to time with millisecond accuracy (or better), then look into other ways.

Related

Do While with Multiple Conditions

Simple Question, I must just be missing something obvious.
I am trying to create a subroutine that polls for a window to be open and I am aiming to accomplish this by a loop that will run while two conditions are met. I keep getting an error:
Run time error 13: Type Mismatch
On the Do while loop, and after searching through the similar issues on SO I still am not quite sure what I am doing wrong.
Here is the line that keeps erroring out:
Sub FindHWND()
Dim HWNDOut as string
Dim Timer as Date
Timer = Now()
'This following line is the one erroring out.
Do While ((Now() < Timer + TimeValue("00:00:10")) And (HWNDOut = 0))
HWNDOut = CStr(Hex(FindWindowEx(0&, 0, "SunAwtFrame", "Graph.graphml - yEd")))
Debug.Print HWNDOut
Sleep (100)
Loop
'Other, following sub and code that is not relevant
End Sub
Where Timer is the Now() at moment before the loop starts, and HWNDOut is the handle for the window I am looking for, which will be found in the loop.
All this loop does is look every 100 MS to see if the window to a third party program has opened, in order to prevent the loss of commands in the next subroutines.
Post Script: If anyone has any suggestions how to do this better, I'm all ears. This is my first time using UI Automation so I'm still learning.
Edit: Added more code to the block for context.
With:
And (HWNDOut = 0))
you are comparing a String to a numeric value..............this will fail.
Try changing variable name Timer to something else e.g. StartTime (think there is a Timer function in VBA which returns a value of a different type; so best not to use words which are reserved or semantically significant).
Might help, might not, good luck.

Create Millisecond Loops in Excel VBA

Since I just found out about Excel Macros, I want to try to simulate moving objects. I would like to run some looped code every 'frame' of my project. I can make an infinite loop in Excel VBA with this code:
Do While True:
'code
Loop
However, this crashes Excel. Is there a way to make an infinite loop that runs every ten milliseconds or so, something like this:
Dim timer as Timer
If timer = 10 Then
'code
timer = 0
End If
EDIT: Your answers are very good, but not exactly what I'm looking for. I want to be able to run other code at the same time; a bit like Javascript's
setInterval(function(){}, 200);
which can run multiple functions simultaneously.
You can use an API call and Sleep.
Put this at the top of your module:
Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Then you can call it in a procedure like this:
Do While True:
Sleep 10 'wait 0.01 seconds
Loop
If the code is in 64bit OS, you will need to use PtrSafe. See https://support.microsoft.com/en-us/help/983043/compile-error-the-code-in-this-project-must-be-updated-for-use-on-64
Your original method is crashing Excel because it is creating an infinite loop with no exit condition.
The second method doesn't work because your system clock time (given by Timer) will never be 10, if you use debug.Print(Timer) in your Immediate Window you will see its value.
Here is some commented code to execute some actions based on a timer. Please please PLEASE make sure you retain the runtime condition for exiting the while loop, infinite loops are the devil and really you should have some other exit code in here somewhere!
Sub timeloop()
Dim start As Double: start = Timer ' Use Timer to get current time
Dim t As Double: t = start ' Set current time "t" equal to start
Dim interval As Double: interval = 1 ' Interval for loop update (seconds)
Dim nIntervals As Long: nIntervals = 0 ' Number of intervals passed
' Use this While loop to avoid an infinite duration! Only runs for "runtime" seconds
Dim runtime As Double: runtime = 10
Do While t < start + runtime
' Check if a full interval has passed
If (t - start) / interval > nIntervals Then
nIntervals = nIntervals + 1
' Do stuff here ---
Debug.Print (t)
' -----------------
End If
t = Timer ' Update current time
Loop
End Sub

time steps in sub and write value to console

Some sub's in my VB.Net program take a lot of time, and I do not understand why. I'd like to start a counter at the beginning of the sub and write the Milliseconds passed for each row in the console.
I tried using a Timer and resetting it after each row but it was too threadintesive. Is there a better way, using the system date/time in order to get a quite precise reading of the time each step takes?
Thank you
Use the Stopwatch class. However doing so for each row seems a bit impossible... That means you would have to add for example Console.WriteLine() after every row.
Example usage:
Dim sw As New Stopwatch
sw.Start()
yourMethod()
sw.Stop()
Console.WriteLine(String.Format("{0} ms", sw.Elapsed.TotalMilliseconds))

VB.net Hour(Now()) Not Working

I am trying to make a vb.net program which at a specific time that the user has chosen, the code will excecute. To do this, I need to check every minute to check every minute if the hour and minute the user has entered are matching to the current time (unless there is a better way to do this). I tried to use
Dim CurrentHour As Integer = Hour(Now())
But the program gives me an error message saying,
Expression is not an array or method, and cannot have an argument list
I am going to use a Do Loop to check, but of course to see if the two are matching, I need the current Hour and Minute
Your code is correct. What you need to watch our for is stuff like this:
Dim Now As Date
Dim CurrentHour = Hour(Now())
Which produces error BC30471: Expression is not an array or a method, and cannot have an argument list.
You see the problem by now perhaps, the Now variable hides the Now function. The compiler now gets confuzzled, it doesn't understand why the parentheses are present. And correctly complains that Now is not an array and not a method. It isn't, not anymore.
Other than renaming the variable, you can also solve it by giving a more complete name:
Dim CurrentHour = Hour(DateAndTime.Now())
Although that gets to be fairly obscure, using DateTime.Now instead is the .NET way instead of the Basic way.
You should use the native DateTime properties:
Dim CurrentHour As Integer = Now().Hour
If you want to use the Hour method, you may need to fully qualify it to be:
Microsoft.VisualBasic.Hour(Now())
because Hour is most likely a property or method elsewhere in your application.
Dim Inputtime As DateTime
if Inputtime = Date.Now.Hour Then
MsgBox("Success!")
End If
I wouldn't use a do loop as it will consume all of the memory for the program. I would go with a timer that ticks once every miunute. and have it fire this sub routine.
Task Scheduler is an option. I rather use Marshal.FinalReleaseComObject then the loop in the bottom of the code, and GC.Collect needs to be called again after GC.WaitForPendingFinalizers()

Loop to check time in VB.NET

So I'm kind of new to VB and am just playing around with a little project, I currently need a loop that is constantly checking the systems clock to see if it's equal to a certain time.
While Not myTime.Hour = 24
If TimeOfDay = newTime Then
nfi.ShowBalloonTip(15)
intRandNumb = RandomNumber(1, 15)
dblAddMinutes = intTime + intRandNumb
newTime = TimeOfDay.AddMinutes(dblAddMinutes)
End If
End While
I have this right now, but obviously it's grinding everything to a halt and using 50% of my cpu in the process, I just would like to know what I can substitute in or change to make this loop run better and perform how I need it to.
you can add
Threading.Thread.Sleep(0),
this will cause a context switch and greatly reduce the CPU usage
Also consider using a timer object to be called every 10 or 100 ms, this will also be better in usage then having a loop
You can use
Threading.Thread.Sleep(0)
This will cause the working thread to yield the rest of it's current timeslice which will reduce the cpu usage quite a bit. However you should consider whether you really nead busy waiting for the time or if you could get away with setting a timer to count down the difference between the current time and the expected time, e.g.:
var t = new System.Timers.Timer((DateTime.Now - DateTime.Now).TotalMilliseconds);
t.Elapsed = DoSomething;
t.Start();
checking the systems clock to see if it's equal to a certain time.
There are two "correct" ways to do this:
Build a normal app that doesn't care what time it is, and set it up in windows as a schedule task.
Check the time once and calculate how long until the desired time. Then set up a timer to wait for that exact duration.
Under no circumstance should you keep polling the system clock for something like this that will just run once.
As Joel pointed out, you should try using a timer instead. I'm not sure if your app is a form or console or other, so I'll try to be generic and use System.Timers.Timer.
The code here (interval is set at 10ms, change to a value of your need):
Private timer1 As System.Timers.Timer
Const interval As Integer = 10
Sub initTimer()
timer1 = New System.Timers.Timer(10)
AddHandler timer1.Elapsed, AddressOf Me.timer_Elapsed
timer1.Start()
End Sub
Sub timer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
'do your stuff here
'Console.WriteLine(e.SignalTime.ToString())
End Sub