I quickly wrote this little rinky-dink vb.net console app to demonstrate something to someone. When I got to looking at it I thought there must be a better way than eating up cycles by using
While True
...
End While
but I have no idea what it is. Any thoughts?
Module ControlExecutive
Private WithEvents MyTimer As New Timers.Timer
Sub Main()
MyTimer.Interval = 10000
Console.WriteLine("Start timer, interrupt every 10000 ms")
MyTimer.Start()
OuterLoop()
End Sub
Sub OuterLoop()
While True
'wait for timer interrupts
End While
End Sub
Sub HandleTimerInterrupt(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles MyTimer.Elapsed
Console.WriteLine(String.Format("Interrupt at {0}", DateTime.Now))
End Sub
End Module
Instead of (or inside) a loop for waiting, you should use System.Threading.Thread.Sleep(milliseconds). This will pause the thread without causing a CPU spike.
Related
I'm making a simple multithreading program to explain the working of threading. I want two counters counting on the same time but it doesn't work.
It only works if I use: CheckForIllegalCrossThreadCalls = False. But, I want to program in a proper way.
Code:
Dim Thread1 As System.Threading.Thread
Dim Thread2 As System.Threading.Thread
Private Delegate Sub SetTeller1()
Private Sub teller1()
If teller1Label.InvokeRequired Then
Invoke(New SetTeller1(AddressOf teller1))
Else
For i As Integer = 0 To 1000
teller1Label.Text = i
Refresh()
Next
End If
End Sub
Delegate Sub SetTeller2()
Private Sub teller2()
If teller2Label.InvokeRequired Then
Invoke(New SetTeller2(AddressOf teller2))
Else
For i As Integer = 0 To 1000
teller2Label.Text = i
Refresh()
Next
End If
End Sub
Private Sub teller1Button_Click(sender As Object, e As EventArgs) Handles teller1Button.Click
Thread1 = New Threading.Thread(AddressOf teller1)
Thread1.Start()
End Sub
Private Sub teller2Button_Click(sender As Object, e As EventArgs) Handles teller2Button.Click
Thread2 = New Threading.Thread(AddressOf teller2)
Thread2.Start()
End Sub
The multithreading works perfectly, but you are not utilizing it. The only thing you're currently doing in the background thread is calling Invoke, which means that your thread will exit within a few milliseconds and then be discarded.
Once you call Invoke the execution of the teller1 or teller2 method is moved to the UI thread, meaning it will block the UI until its execution is finished. You should only invoke when you are to update the UI, and perform all iterations in the background thread.
Here's an example of how you can do it more properly:
Delegate Sub SetTeller1(ByVal Text As String)
Private Sub teller1()
For i As Integer = 0 To 1000
SetTeller1Text(i)
Next
End Sub
Private Sub SetTeller1Text(ByVal Text As String)
If Me.InvokeRequired Then
Me.Invoke(New SetTeller1(AddressOf SetTeller1Text), Text)
Else
teller1Label.Text = Text
Me.Refresh()
End If
End Sub
For improved readability I changed for example Invoke(...) to Me.Invoke(...).
Also I'm not sure why you're calling Refresh() as it isn't necessary and will just cause extra redrawing of the entire container (guessing this is a form).
I am trying a simple code in Silk4Net using VB.Net. I have automated launching of a calculator. Before the numbers can be typed, a message box appears. I am unable to find a way to dismiss the message box automatically. I want to be able to recognize the message box and either push it to the back or dismiss it totally.
The code is as below:
<TestMethod()>
Public Sub TestMethod1()
With _desktop.Window("Calculator")
.SetActive()
generateMsg()
.PushButton("Clear").Select()
.PushButton("3").Select()
.PushButton("5").Select()
End With
End Sub
Public Sub generateMsg()
Thread.Sleep(2000)
With _desktop.Window(MsgBox("Test", MsgBoxStyle.Critical, "Test"))
For Each p As Process In Process.GetProcesses
If p.MainWindowTitle.Contains("Test") Then
p.Kill()
End If
Next
'With .Dialog("Test")
' '.PushButton("OK").Select()
'End With
' .Close()
End With
End Sub
Any help would be much appreciated. Thanks.
Updated answer
You could add a timer to the code that uses SendKeys.SendWait - like this - adapting it a little for your test environment as I'm not sure about Silk4Net tbh
Dim WithEvents timer1 As New System.Timers.Timer
timer1.Interval = 5000
timer1.Enabled = True
MsgBox("Hello. I will go bye-bye in 5 seconds.")
timer1.Enabled = False
And as a separate sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles timer1.Elapsed
SendKeys.SendWait("{ENTER}")
End Sub
I have the following code:
Dim p() As Process
Private Sub CheckIfRunning()
p = Process.GetProcessesByName("skype") 'Process name without the .exe
If p.Count > 0 Then
' Process is running
MessageBox.Show("Yes, Skype is running")
Else
' Process is not running
MessageBox.Show("No, Skype isn't running")
End If
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
CheckIfRunning()
End Sub
And it works GREAT!
But I'm wondering how I would convert this to a monitoring application, to constantly check if the processes is running. Is it as simple as putting the check on a timer every 1 second, or is there a better, more efficient way to go about this.
In the end result, I'd like to have a label that says "Running", or "Not Running" based on the process, but I need something to watch the process constantly.
If you need the app running all the time, then you don't need a Timer at all. Subscribe to the Process.Exited() event to be notified when it closes. For instance, with Notepad:
Public Class Form1
Private P As Process
Private FileName As String = "C:\Windows\Notepad.exe"
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim ps() As Process = Process.GetProcessesByName(Path.GetFileNameWithoutExtension(FileName))
If ps.Length = 0 Then
P = Process.Start(FileName)
P.EnableRaisingEvents = True
AddHandler P.Exited, AddressOf P_Exited
Else
P = ps(0)
P.EnableRaisingEvents = True
AddHandler P.Exited, AddressOf P_Exited
End If
End Sub
Private Sub P_Exited(sender As Object, e As EventArgs)
Console.WriteLine("App Exited # " & DateTime.Now)
Console.WriteLine("Restarting app: " & FileName)
P = Process.Start(FileName)
P.EnableRaisingEvents = True
AddHandler P.Exited, AddressOf P_Exited
End Sub
End Class
That would keep it open all the time, assuming you wanted to open it if it wasn't already running.
If you don't want to open it yourself, and need to detect when it does open, then you could use WMI via the ManagementEventWatcher as in this previous SO question.
I've done something similar to this to monitor an exe that I need to be running all the time, and to restart it if it was down.
Mine was running as a Windows Service - that way it would start when windows booted and id never need to look after it.
Alternatively you could just create it as a console app and put it in your startup folder?
I had:
Sub Main()
Do
Check_server()
Dim t As New TimeSpan(0, 15, 0)
Threading.Thread.Sleep(t)
Loop
End Sub
Public Sub Check_server()
Dim current_pros() As Process = get_pros()
Dim found As Boolean = False
If Now.Hour < "22" Then
For Each pro In current_pros
If pro.ProcessName.ToLower = "Lorraine" Then
found = True
Exit For
Else
found = False
End If
Next
If found Then
Console.WriteLine("Server up")
Else
Console.WriteLine("Server down - restarting")
restart_server()
End If
End If
End Sub
My "server" app was called Lorraine...Also a timer maybe better practice than having the thread sleep..
From my experience, a simple timer works best:
'Timer interval set to 1-5 seconds... no remotely significant CPU hit
Private Sub timerTest_Tick(sender As System.Object, e As System.EventArgs) Handles timerTest.Tick
Dim p() As Process = Process.GetProcessesByName("Skype")
lblStatus.Text = If(p.Length > 0, "Skype is running.", "Skype isn't running.")
End Sub
Your mileage may vary, but I don't like to deal with separate threads unless necessary.
I have this code which controls an equipment I'm building. My main Sub sends several comands to the hardware, always waiting the necessary time to perform the action. The algorithm is something like:
Private Sub StartAuto()
Send command1 to hardware and wait x seconds
Send command2 to hardware and wait y seconds
Send command3 to hardware and wait z seconds
etc
End Sub
The thing is, I want to be able to stop the execution of this routine with a Stop button. So far I couldn't think or find any better method than checking a boolean between every command, which would be terrible:
Private Sub btStopAuto_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btStopAuto.Click
keepRunning = false
End Sub
Private Sub StartAuto()
keepRunning = True
Send command1 to hardware and wait x seconds
If (keepRunning = false)
Exit Sub
End If
Send command2 to hardware and wait y seconds
If (keepRunning = false)
Exit Sub
End If
Send command3 to hardware and wait z seconds
If (keepRunning = false)
Exit Sub
End If
etc
End Sub
I thought about using While loops, but they will only validate after the whole procedure is done.
Essentialy what I need is something like
Private Sub btStopAuto_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btStopAuto.Click
StartAuto.Exit
End Sub
Obviously this doesn't exist, any ideas on how it could be done? Right now, if I notice a problem during the procedure I have to abort the debugging, but that won't work outside development.
Thank you.
If you called AutoStart() in another tread, you could then cancel the thread.
The boolean flag is the best/easiest way but the thread is an option.
I am a VB6 coder and I'm making the move to VB8 / VB.NET
I know how to wait in VB6, but my problem is that I don't know how to wait in VB8/VB.NET. I have a TextBox called textbox2 that contains the number of seconds I want to wait. I used to use wait 60 in VB6, but of course VB2008 is different.
Can anyone help me to do this?
I know this is an old question, but I thought there were so many conflicting answers and I thought the solution I use is simple and straightforward enough.
Also, I wrote this when I switched from VB6 to .net, for the same reason as OP.
Private Sub Wait(ByVal seconds As Long)
Dim dtEndTime As DateTime = DateTime.Now.AddSeconds(seconds)
While DateTime.Now < dtEndTime
Application.DoEvents()
End While
End Sub
Use this do that your UI does not hang.
For i = 1 to 300
threading.thread.sleep(i * 1000)
application.doevents
next
[edit: I re-read the question and see it was specifically asking about a TextBox named textbox2 so I've updated the answer to reflect that.]
Well, I think one answer would be to use:
System.Threading.Thread.Sleep(Int32.Parse(textbox2.Text) * 1000);
if your text box contains the number of seconds to wait. However if you aren't in a background thread, this will hang your application up for the amount of time you are waiting.
You could also do something like:
Dim StartTime As DateTime
StartTime = DateTime.Now
While (DateTime.Now - StartTime) < TimeSpan.FromSeconds(Int32.Parse(textbox2.Text))
System.Threading.Thread.Sleep(500)
Application.DoEvents()
End While
which would not hang up the UI while you wait. (Also, you could use Convert.Int32(textbox2.Text) to convert the data in the textbox.)
Oh, and in certain cases, another way you can avoid the issues with the UI locking up would be do implement a timer callback instead. (see http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx for some more detail.) To do this, you
do whatever processing you need to do before the pause
create a function that picks up processing where you left off
create a timer that calls your function afterward
Code:
Public Class MyClass
Public MyTimer As System.Timers.Timer
Public Sub OnWaitCompleted(source As Object, e As ElapsedEventArgs)
MyTimer.Stop()
MyTimer = Nothing
DoSecondPartOfProcessing()
End Sub
Public Sub DoFirstPartOfProcessing()
' do what you need to do before the wait
MyTimer = New System.Timers.Timer(Int32.Parse(textbox2.Text))
AddHandler MyTimer.Elapsed, AddressOf OnWaitCompleted
MyTimer.Start()
End Sub
Public Sub DoSecondPartOfProcessing()
' do what you need to do after the wait
End Sub
End Class
try to use a timer
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
Dim interval As Integer = 0
If Integer.TryParse(Me.TextBox2.Text, interval) Then
Timer1.Enabled = True
Timer1.Interval = interval
Timer1.Start
End If
End Sub
Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
'your code here
End Sub
In the 'event of the timer ticks then implements the logic to invoke the method Timer.Stop (), but this depends on what you do.
Regards.
Use Thread.Sleep:
Thread.Sleep(60000)
Update, following comment:
To retrieve and convert the value of the text box use:
Dim sleepValue As Integer = Integer.Parse(textbox2.Text)
This will throw an exception if the value cannot be converted.
I don't know why do you want it and and why you don't use threads, but this sleep function act similar wait in vb6:
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
MsgBox("begin")
Sleep(2000)
MsgBox("end")
End Sub