I did this first into a WinForm project, now I've changed the application type to "Console application", I've deleted the form1.vb, changed the startup object to this "Module1.vb" but now I can't run the app.
well the app runs but the timer tick is doing nothing, the code is exactly the same, I only did one change for the sub main/form1_load name
What I'm doing wrong?
PS: I've tested if the error was in the conditional of the lock method and all is good there, the problem is with the ticker event but I don't know why.
#Region " Vars "
Dim Running As Boolean = False
Dim Errors As Boolean = False
Dim Executable_Name As String = Nothing
Dim Toogle_Key As System.Windows.Forms.Keys = Nothing
Dim WithEvents Toogle_Key_Global As Shortcut = Nothing
Dim Executable_Timer As New Timer
Dim Lock_Timer As New Timer
Dim Lock_Interval As Int32 = 10
Dim Lock_Sleep As Int32 = Get_Milliseconds(3)
Dim Screen_Center_X As Int16 = (Screen.PrimaryScreen.Bounds.Width / 2)
Dim Screen_Center_Y As Int16 = (Screen.PrimaryScreen.Bounds.Height / 2)
#End Region
' Load
Sub main()
Pass_Args()
Sleep()
Lock()
End Sub
' Lock
Private Sub Lock()
If Process_Is_Running(Executable_Name) Then
AddHandler Lock_Timer.Tick, AddressOf Lock_Tick
AddHandler Executable_Timer.Tick, AddressOf Executable_Tick
Lock_Timer.Interval = Lock_Interval
Lock_Timer.Start()
Executable_Timer.Start()
Running = True
Else
Terminate()
End If
End Sub
' Lock Tick
Private Sub Lock_Tick()
Console.WriteLine("test")
If Running Then Cursor.Position = New Point(Screen_Center_X, Screen_Center_Y)
End Sub
UPDATE
I made these changes like in the examples of MSDN:
Dim Executable_Timer As New System.Timers.Timer
Dim Lock_Timer As New System.Timers.Timer
AddHandler Lock_Timer.Elapsed, AddressOf Lock_Tick
AddHandler Executable_Timer.Elapsed, AddressOf Executable_Tick
But the tick/elapsed is still doing nothing...
FROM MSDN
Windows.Forms.Timer
Implements a timer that raises an event at user-defined intervals.
This timer is optimized for use in Windows Forms applications and must
be used in a window.
You need a System.Timer
Of course this requires a different event Handling
(Example taken from MSDN)
' Create a timer with a ten second interval.
Dim aTimer = new System.Timers.Timer(10000)
' Hook up the Elapsed event for the timer.
AddHandler aTimer.Elapsed, AddressOf OnTimedEvent
....
Private Shared Sub OnTimedEvent(source As Object, e As ElapsedEventArgs)
Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime)
End Sub
You could use Windows.Forms.Timer in console application if you add Application.Run() at the end of your main().
This kind of timer might be useful in some console applications if you are using any offscreen Windows.Forms object - ie.: for offscreen rendering - these objects can't be simply accessed from System.Timer since it fires on separate thread (than the one where Windows.Forms object was created on).
Otherwise by all means use the System.Timers.Timer or System.Threading.Timer
Related
I want to use a backgroundworker to poll a hardware sensor very frequently without leaving my UI inoperable.
Because the backgroundworker simply polls until interrupted - runtime is purely dictated by the user interrupting it - it has no change in progress so to speak.
If I call ReportProgress with a constant value, e.g. ReportProgress(1), will this still call ProgressChanged? I require ProgressChanged to update the UI in accordance with the latest poll data.
The value passed as first parameter to ReportProgress just serves at your code on the UI thread to display the advancement of your background task.
It has no importance for the execution of the call to ProgressChanged.
If you need to communicate some different data to your ProgressChanged event you could use the overload of ReportProgress that takes two arguments and allows to pass the instance of a custom object as second parameter.
In this very trivial example, I have defined a class named WorkingStatus with just one property that I change in the DoWork method, then I pass an instance of this class to the ProgressChanged event. Of course your WorkingStatus class could be more complex with all the informations that you want to display on the UI thread
public class WorkingStatus
public Current as Integer
'.... other properties as needed....
End Class
Sub Main
Dim bkw = new BackgroundWorker()
bkw.WorkerReportsProgress = true
AddHandler bkw.ProgressChanged, AddressOf bgw_ProgressChanged
AddHandler bkw.DoWork, AddressOf bgw_DoWork
bkw.RunWorkerAsync()
' This loop just to avoid the immediate close of the example
Dim counter = 0
While (bkw.IsBusy)
counter+=1
Console.WriteLine("IsBusy " & counter.ToString())
Thread.Sleep(150)
End While
End Sub
private sub bgw_DoWork(sender as object, e as DoWorkEventArgs)
Dim bgw = DirectCast(sender, BackgroundWorker)
Dim sts = new WorkingStatus() With {.Current = 0}
' A simulation of your inner working
for i = 0 to 10
Thread.Sleep(5000)
sts.Current+=1
bgw.ReportProgress(1, sts)
Next
Console.WriteLine("Background DoWork ENDED")
End Sub
private sub bgw_ProgressChanged(sender as object, e as ProgressChangedEventArgs)
Dim sts = DirectCast(e.UserState, WorkingStatus)
Console.WriteLine("Progress:" & e.ProgressPercentage.ToString() & ", Status=" & sts.Current)
End Sub
I'm trying to create a class that will create a Relogin after certain time but after the first Relogin it keeps populating. Heres my Code:
Private Shared timer As Timer
Public Shared Event DoSomething As Action(Of Integer)
Private Shared _timesCalled As Integer = 0
Public Shared Sub Start()
AddHandler DoSomething, AddressOf EventHandler
timer = New System.Threading.Timer(AddressOf timer_Task, Nothing, 0, 1000)
End Sub
Public Shared Sub [Stop]()
timer.Dispose()
End Sub
Private Shared Sub timer_Task(State As Object)
_timesCalled += 1
If _timesCalled = 15 Then 'Should Raise event every 15s
RaiseEvent DoSomething(_timesCalled)
End If
End Sub
Private Shared Sub EventHandler(ByVal EventNumber As Integer)
My.Application.Dispatcher.Invoke(New Action(AddressOf OpenLogin))
End Sub
Private Shared Sub OpenLogin() 'This event fires multiple times after the first Event
Dim x As New MainWindow
x.ShowDialog() 'Dialog stops code from continuing.
x = Nothing
_timesCalled = 0
End Sub
Open_Login() fires multiple times after the first or second time. Doesn't seem to cause the same problem when I replace "MainWindow" object with a messagebox. Please Help. Thank you.
Notwithstanding the fact that your issue seems to be solved - using an unsynchronised counter is not a reliable way to have an event fired every predetermined period.
The timer event itself fires from a separate .NET managed thread and subsequently, the _timesCalled variable can be accessed from multiple threads. So it is possible that while you are re-setting _timesCalled=0 from your main thread another thread from the default threadpool is about to overwrite this with _timesCalled=14.
In your specific example it is simpler and more straightforward to reschedule the timer event after you’ve finished handling one. That way you can also account for the time it took you to process the event and the timer inaccuracies and lag.
Public Shared Sub Start()
...
' assuming this runs only once
timer = New System.Threading.Timer(AddressOf timer_Task, Nothing, 15000, Timeout.Infinite)
End Sub
Private Shared Sub timer_Task(State As Object)
RaiseEvent DoSomething(_timesCalled)
End Sub
Private Shared Sub OpenLogin()
Dim x As New MainWindow
x.ShowDialog()
x = Nothing
' Reschedule the timer again here, adjust the 15000 if necessary, maybe prefer timer.ChangeTime(...) instead
End Sub
Figured out it was my coding. Everytime MainWindow would load it would run Start() creating a new instance of Timer. Correct issue. Thanks for viewing
net program that uses excel as a datasource. I then fill a datagridview with this datasource and make changes to the dataset via the datagridview. I'm trying to find a way to refresh this dataset via a button that will update the values after a change. My only problem is that I'm trying to set up a timer in my refresh method but it never initializes/starts. I can't figure out why, from what I've found online the way to start a timer in vb.net is to set the timer variable to enabled = true. I've stepped into my debugger and found that the timer never starts. Here is my code below, if there is anyone who can figure out why this timer isn't starting I would greatly appreciate your help!
Dim mytimer As New System.Timers.Timer
Sub refresh()
write2Size()
mytimer.timer = New System.Timers.Timer(20000)
'Starting Timer
mytimer.Enabled = True
Cursor.Current = Cursors.WaitCursor
AddHandler mytimer.Elapsed, AddressOf OnTimedEvent
objworkbook.Save()
objExcel.ActiveWorkbook.Save()
myDS.Clear()
retrieveUpdate()
'Setting the cursor back to normal here
Cursor.Current = Cursors.Default
End Sub
Private Shared Sub OnTimedEvent(source As Object, e As ElapsedEventArgs)
Console.WriteLine("The Elapsed event was raised at {0}, e.SignalTime)
End Sub
You are creating a timer here
Dim mytimer As New System.Timers.Timer
but you only assign a handler to the one you create inside the Refresh routine.
Would have expected
Dim mytimer As New System.Timers.Timer(20000)
AddHandler mytimer.Elapsed, AddressOf OnTimedEvent
then
Private Shared Sub OnTimedEvent(source As Object, e As ElapsedEventArgs)
Console.WriteLine("The Elapsed event was raised at {0}, e.SignalTime)
Refresh()
End Sub
and something like
Sub refresh()
myTimer.Enabled = False
// refresh the doings
myTimer.Enabled = True
End sub
Excuse the lack of VBness, I'm a C# boy.
so i have a problem, when trying to call the Skype API on a timer it gets nothing, im just trying to read messages on a loop and display them in my console, but on timer tick, nothing happens. But if i put the timer tick code in main it works fine. Here is my code:
Imports SKYPE4COMLib
Imports System.Timers.Timer
Module main
Dim oSkype As New SKYPE4COMLib.Skype
Dim aUser = oSkype.User("echo123")
Dim aChat = oSkype.Messages(aUser.Handle)
Dim tmr As New Timers.Timer
Sub Main()
tmr.AutoReset = True
tmr.Interval = 1000
AddHandler tmr.Elapsed, AddressOf tmrTick
tmr.Enabled = True
Console.ReadLine()
End Sub
Public Sub tmrTick(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs)
For Each aMessages In aChat
Console.WriteLine(aMessages.FromHandle & ": " & aMessages.Body)
Next
Console.WriteLine("")
End Sub
End Module
Thanks,
Adam
Timers swallow exceptions, you might be getting an exception in the timer code, most likely a cross thread exception. Try settings visual studio to break on exceptions (debug > exceptions) and see if you get anything.
If you do get a cross thread exception, you wont be able to use System.timer timers as they run on the thread pool. in that case you should use a ui friendly timer such as the ones from winforms/wpf, or a dedicated thread
I have been pulling my hair out trying to get this to work. If I step through the code in debugger it all works great.
My problem is if I just run it, only the last task responds. I'm guessing I am overwriting the background working or something. I am sure I am doing a few things wrong but my code is now messy as I tried many way while searching. I know of the threadpool and .Net 4.0 tasks but having a hard time getting to do what I need.
Basicly I am writing a program (trying more likely) that takes a list of computers and pings then, then checks their uptime and reports back.
This works fine in the UI thread (Obviously that locks up my screen). I can have the background worker just do this, but then it does each computer 1 by one, and while the screen is responsive it still takes a long time.
So my answer was to have a for loop for each server launching a new background worker thread. My solution does not work.
I have seen other threads that I could do it, but I need to use with events to call code to update to UI when each is done.
What is the most simple way to do this?
Here is my code. Most is just copy paste + modify till I get it working right.
So In the main class I have the testworker.
(I tried using Testworker() but it said I could not do that WithEvents)
When I click the button the list loads.
Private WithEvents TestWorker As System.ComponentModel.BackgroundWorker
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
Button1.IsEnabled = False
Dim indexMax As Integer
indexMax = DataGridStatus.Items.Count
For index = 1 To (indexMax)
Dim Temp As ServerInfo = DataGridStatus.Items(index - 1)
Temp.Index = index - 1
Call_Thread(Temp)
Next
End Sub
Private Sub Call_Thread(ByVal server As ServerInfo)
Dim localserver As ServerInfo = server
TestWorker = New System.ComponentModel.BackgroundWorker
TestWorker.WorkerReportsProgress = True
TestWorker.WorkerSupportsCancellation = True
TestWorker.RunWorkerAsync(localserver)
End Sub
Private Sub TestWorker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles TestWorker.DoWork
Dim iparray As IPHostEntry
Dim ip() As IPAddress
Dim Server As ServerInfo
Server = e.Argument
Try
'Get IP Address first
iparray = Dns.GetHostEntry(Server.ServerName)
ip = iparray.AddressList
Server.IPAddress = ip(0).ToString
'Try Pinging
Server.PingResult = PingHost(Server.ServerName)
If Server.PingResult = "Success" Then
'If ping success, get uptime
Server.UpTime = GetUptime(Server.ServerName)
Else
Server.PingResult = "Failed"
End If
Catch ex As Exception
Server.PingResult = "Error"
End Try
TestWorker.ReportProgress(0, Server)
Thread.Sleep(1000)
End Sub
Private Sub TestWorker_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles TestWorker.ProgressChanged
Dim index As Integer
Dim serverchange As ServerInfo = DirectCast(e.UserState, ServerInfo)
index = DataGridStatus.Items.IndexOf(serverchange)
' index = serverchange.Index
DataGridStatus.Items.Item(index) = serverchange
' ProgressBar1.Value = e.ProgressPercentage
DataGridStatus.Items.Refresh()
End Sub
You are only getting the last result because you are blowing away your BackgroundWorker each time you call TestWorker = New System.ComponentModel.BackgroundWorker. Since the processing is being done asynchronously, this line is being called multiple times within your for loop before the previous work has finished.
Something like the following might work. (Sorry, my VB is rusty; there are probably more efficient ways of expressing this.)
Delegate Function PingDelegate(ByVal server As String) As String
Private _completedCount As Int32
Private ReadOnly _lockObject As New System.Object
Dim _rnd As New Random
Private _servers As List(Of String)
Private Sub GoButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles GoButton.Click
_servers = New List(Of System.String)(New String() {"adam", "betty", "clyde", "danny", "evan", "fred", "gertrude", "hank", "ice-t", "joshua"})
_completedCount = 0
ListBox1.Items.Clear()
GoButton.Enabled = False
BackgroundWorker1.RunWorkerAsync(_servers)
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim servers As List(Of System.String) = DirectCast(e.Argument, List(Of System.String))
Dim waitHandles As New List(Of WaitHandle)
For Each server As System.String In servers
' Get a delegate for the ping operation. .Net will let you call it asynchronously
Dim d As New PingDelegate(AddressOf Ping)
' Start the ping operation async. When the ping is complete, it will automatically call PingIsDone
Dim ar As IAsyncResult = d.BeginInvoke(server, AddressOf PingIsDone, d)
' Add the IAsyncResult for this invocation to our collection.
waitHandles.Add(ar.AsyncWaitHandle)
Next
' Wait until everything is done. This will not block the UI thread because it is happening
' in the background. You could also use the overload that takes a timeout value and
' check to see if the user has requested cancellation, for example. Once all operations
' are complete, this method will exit scope and the BackgroundWorker1_RunWorkerCompleted
' will be called.
WaitHandle.WaitAll(waitHandles.ToArray())
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
ListBox1.Items.Add(String.Format("{0} ({1}% done)", e.UserState, e.ProgressPercentage))
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
GoButton.Enabled = True
End Sub
Private Function Ping(ByVal server As System.String) As System.String
' Simulate a ping with random result and duration
Threading.Thread.Sleep(_rnd.Next(1000, 4000))
Dim result As Int32 = _rnd.Next(0, 2)
If result = 0 Then
Return server & " is ok"
Else
Return server & " is down"
End If
End Function
Private Sub PingIsDone(ByVal ar As IAsyncResult)
' This method is called everytime a ping operation completes. Note that the order in which
' this method fires is completely independant of the order of the servers. The first server
' to respond calls this method first, etc. This keeps optimal performance.
Dim d As PingDelegate = DirectCast(ar.AsyncState, PingDelegate)
' Complete the operation and get the result.
Dim pingResult As String = d.EndInvoke(ar)
' To be safe, we put a lock around this so that _completedCount gets incremented atomically
' with the progress report. This may or may not be necessary in your application.
SyncLock (_lockObject)
_completedCount = _completedCount + 1
Dim percent As Int32 = _completedCount * 100 / _servers.Count
BackgroundWorker1.ReportProgress(percent, pingResult)
End SyncLock
End Sub
Update: I posted this answer focusing on exactly what you were trying to do from a technical standpoint (use many background workers) without really putting much thought into whether or not this was a good way to accomplish your real objective. In fact, I think you could achieve what you're going for much more easily with a single BackgroundWorker and something like a Parallel.ForEach loop in its DoWork event handler (this takes care of a lot of the nitty gritty work in, e.g., Dave's solution).
When you declare WithEvents TestWorker As BackgroundWorker in VB it wraps it up something like this (not exactly—this is just to illustrate the idea):
Private _TestWorker As BackgroundWorker
Private Property TestWorker As BackgroundWorker
Get
Return _TestWorker
End Get
Set(ByVal value As BackgroundWorker)
' This is all probably handled in a more thread-safe way, mind you. '
Dim prevWorker As BackgroundWorker = _TestWorker
If prevWorker IsNot Nothing Then
RemoveHandler prevWorker.DoWork, AddressOf TestWorker_DoWork
' etc. '
End If
If value IsNot Nothing Then
AddHandler value.DoWork, AddressOf TestWorker_DoWork
' etc. '
End If
_TestWorker = value
End Set
End Property
When you realize this, it becomes clear that by setting TestWorker to a new BackgroundWorker on every call to Call_Thread, you are removing any attached handlers from the object previously referenced by the field.
The most obvious fix would simply be to create a new local BackgroundWorker object in each call to Call_Thread, attach the handlers there (using AddHandler and RemoveHandler), and then just let it do its thing:
Private Sub Call_Thread(ByVal server As ServerInfo)
Dim localserver As ServerInfo = server
' Use a local variable for the new worker. '
' This takes the place of the Private WithEvents field. '
Dim worker As New System.ComponentModel.BackgroundWorker
' Set it up. '
With worker
.WorkerReportsProgress = True
.WorkerSupportsCancellation = True
End With
' Attach the handlers. '
AddHandler worker.DoWork, AddressOf TestWorker_DoWork
AddHandler worker.ProgressChanged, AdressOf TestWorker_ProgressChanged
' Do the work. '
worker.RunWorkerAsync(localserver)
End Sub
Creating the worker right there in the method should be fine as long as you do so from the UI thread, since BackgroundWorker automatically attaches to the current SynchronizationContext in its constructor (if I remember correctly).
Ideally you should use only 1 backgroundworker and use it like this:
Assemble all the work that needs to be done: in your case a list of ServerInfo
Do the work in the background: ping all the servers and keep the result
Report progress: for example after each server pinged
Put results back in DoWorkEventArgs.Result
Display the results back in your UI.
You need to attach TestWorker_DoWork and TestWorker_ProgressChanged to the DoWork and ProgressChanged events within Call_Thread. I haven't yet examined the rest of the code, but that is why it isn't doing anything now.
TestWorker = New System.ComponentModel.BackgroundWorker
TestWorker.WorkerReportsProgress = True
TestWorker.WorkerSupportsCancellation = True
AddHandler TestWorker.DoWork, AddressOf TestWorker_DoWork
AddHandler TestWorker.ProgressChanged, AddressOf TestWorker_ProgressChanged
TestWorker.RunWorkerAsync(localserver)