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
Related
I have a simple WinForm application. The main entry point of the application is mainForm. I am using a Timer on the form and the timer interval is being set to 2000ms. The Tick event of the Timer is as below,
Public myValue as Integer = 100
Private Sub myTimer_Tick(sender As Object, e As EventArgs) Handles myTimer.Tick
If myValue = 0 Then
myTimer.Enabled = False
Else
myValue = myValue -1
End If
End Sub
The timer is being called at the start of the application when mainForm is loaded. Now myValue is a global variable and here for the purpose of simplicity I have used this otherwise it is replaced by some process count mechanism which is not required to be explained here.
I am able to use this approach as long as I am using Windows.Forms.Timer placed on some specific Form. I have two more scenarios in which this approach fails.
1 - I have to use the same functionality on some other form and for this currently I am using a separate Timer on another Form and it has its own Tick event.
2 - I have to use the same functionality from another module/class and I am unable to achieve this because for this to work I require a Form.
Now for a start I have looked into Threading.Timer. The problem I am facing is that I don't know how to wait for Threading.Timer to finish as the control goes to next line after Threading.Timer is called. I am not sure whether this can be done with the help of WaitHandle or not. Also I have read that Threading.Timer creates a separate Thread for each of its Tick. This seems like an overkill in my simple scenario.
I just want to use the Timer functionality without the need of Form. Also I could create the similar functionality using a Do Loop with Thread.Sleep inside it but unless I am sure that my Timer functionality is not going to work in other situations I am going to stick to my Timer approach.
I see ... If thats the case, you should really create a second thread that runs a loop. That thread has some exiting parameters that indicates that operation is completed and the Thread itself is set to Isbackground = false.
However, you could also do this ...
Imports System.Timers
Public Class Main
Private Shared WithEvents m_oTimer As Timers.Timer = Nothing
Private Shared m_oWaitHandle_TimerHasCompleted As System.Threading.AutoResetEvent = Nothing
Public Shared Sub Main()
Try
'Application Entry point ...
'Create the global timer
m_oTimer = New Timers.Timer
With m_oTimer
.AutoReset = True
.Interval = 2000
.Start()
End With
'Create the WaitHandle
m_oWaitHandle_TimerHasCompleted = New System.Threading.AutoResetEvent(False)
'Show your form
Dim oFrm As New Form1
Application.Run(oFrm)
'Wait for the timer to also indicate that it has finished before exiting
m_oWaitHandle_TimerHasCompleted.WaitOne()
Catch ex As Exception
'Error Handling here ...
End Try
End Sub
Private Shared Sub m_oTimer_Elapsed(sender As Object, e As ElapsedEventArgs) Handles m_oTimer.Elapsed
'Timer will fire here ...
Try
If 1 = 2 Then
m_oWaitHandle_TimerHasCompleted.Set()
End If
Catch ex As Exception
'Error Handling ...
End Try
End Sub
End Class
Please note that 'm_oWaitHandle_TimerHasCompleted.Set()' will never run, you'll have to add a condition ... however, once run, the WaitOne will complete and the application will exit as required.
Hows zat?
Sounds to me like you want to create a single instance of a timer, that does not need to be instantiated via a form?
If so ... Create a new class called 'Main' and copy the following into it.
Imports System.Timers
Public Class Main
Private Shared WithEvents m_oTimer As Timers.Timer = Nothing
Public Shared Sub Main()
Try
'Application Entry point ...
'Create the global timer
m_oTimer = New Timers.Timer
With m_oTimer
.AutoReset = True
.Interval = 2000
.Start()
End With
'Show your form
Dim oFrm As New Form1
Application.Run(oFrm)
Catch ex As Exception
'Error Handling here ...
End Try
End Sub
Private Shared Sub m_oTimer_Elapsed(sender As Object, e As ElapsedEventArgs) Handles m_oTimer.Elapsed
'Timer will fire here ...
Try
Catch ex As Exception
'Error Handling ...
End Try
End Sub
End Class
Once done, right click on your project and select 'Properties'. In the Application tab you'll see a checkbox called 'Enable Application framework'. Uncheck this box. Now, in the dropdown called 'Startup Object' you should now see 'Sub Main' .... Select that.
When the application runs, Sub Main will now run instead of your form.
This will create the Timer that will fire outside of your form. Please note, as you're not syncing it, I believe it'll run inside a thread so be a little careful there :)
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
I’m working on a scheduler like project using VB.Net, the application start from “Sub Main” with Application.Run(), all program codes are handler in a class, which is created and started here,
Public Sub Main()
m_App = New myApp
m_App.Start()
Application.Run()
End Sub
Inside the myApp, there has a timer to control the execution of task, and it will start a thread for each task, when the task complete, we try to display the alert window if there has error detected. We have tested two different way for communization between the execute thread and the main thread in order to display the alert window (frmAlert):
1) by adding pulic event in task object, then addhandler to the function in main thread
2) use delegate to notify the main thread
However, the alert window cannot be shown and there has no error reported. After debuging with the IDE, it was found that the alert windows has successful displayed, but it will closed when the task thread is completed.
Here is a simplified task class (testing with two communization methods),
Public Class myProcess
Public Event NotifyEvent()
Public Delegate Sub NotifyDelegate()
Private m_NotifyDelegate As NotifyDelegate
Public Sub SetNotify(ByVal NotifyDelegate As NotifyDelegate)
m_NotifyDelegate = NotifyDelegate
End Sub
Public Sub Execute()
System.Threading.Thread.Sleep(2000)
RaiseEvent NotifyEvent()
If m_NotifyDelegate IsNot Nothing Then m_NotifyDelegate()
End Sub
End Class
And the main application class
Imports System.Threading
Public Class myApp
Private WithEvents _Timer As New Windows.Forms.Timer
Private m_Process As New myProcess
Public Sub Start()
AddHandler m_Process.NotifyEvent, AddressOf Me.NotifyEvent
m_Process.SetNotify(AddressOf NotifyDelegate)
ProcessTasks()
End Sub
Private Sub Timer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _Timer.Tick
ProcessTasks()
End Sub
Public Sub ProcessTasks()
_Timer.Enabled = False
'
Dim m_Thread = New Thread(AddressOf m_Process.Execute)
m_Thread.Start()
'
_Timer.Interval = 30000
_Timer.Enabled = True
End Sub
Public Sub NotifyEvent()
frmAlert.Show()
End Sub
Public Sub NotifyDelegate()
frmAlert.Show()
End Sub
End Class
It was found that the frmAlert is shown by using either NotifyEvent or NotifyDelegate, but it is closed immediately when the Execute is finished.
May I know how we can popup an alert window from the execution thread which can stay in the screen until user close it?
Thanks in advance!
You need to ensure that your main thread does not terminate if you want it to do anything when a sub-thread raises and event.
Public Sub Start()
AddHandler m_Process.NotifyEvent, AddressOf Me.NotifyEvent
m_Process.SetNotify(AddressOf NotifyDelegate)
ProcessTasks()
Do While True 'You might want to add a boolean condition here to instruct the main program to terminate when you want it to.
System.Threading.Thread.Sleep(200)
Loop
End Sub
This will prevent the main thread (program) to end, and thus will be available to handle any events raised by the sub-threads. Note: Your class lacks a termination condition.
How do I call a function every x minutes.
I assume I'd have to add a timer to the form?
here is a simple example:
Public Class SampleCallEveryXMinute
Private WithEvents xTimer as new System.Windows.Forms.Timer
Public Sub New(TickValue as integer)
xTimer = new System.Windows.Forms.Timer
xTimer.Interval = TickValue
End Sub
Public Sub StartTimer
xTimer.Start
End Sub
Public Sub StopTimer
xTimer.Stop
End Sub
Private Sub Timer_Tick Handles xTimer.Tick
SampleProcedure
End Sub
Private Sub SampleProcedure
'SomeCodesHERE
End Sub
End Class
USAGE:
Dim xSub as new SampleCallEveryXMinute(60000) ' 1000 ms = 1 sec so 60000 ms = 1 min
xSub.StartTimer
Yes, you could add a timer to the form, and set its interval to x*60000, where x is the number of minutes between calls.
Remember that the timer runs on the UI thread, so don't do anything intensive in the function. Also, if the UI thread is busy, the timer event will not fire until the UI thread finishes whatever event it is currently processing. If your function is going to be CPU-intensive, then consider having the timer start up a background worker
If you require a longer time period between function calls (ie, one thats too big for a timer interval) then you could have a timer function that fires every minute, and increments a counter until the desired amount of time has passed, before going on to call the function.
ALTERNATIVE 1
Here is good guide to use the Timer Control in VB.net.
The advantage is that you don't have to worry about modifying UI objects from non UI thread.
ALTERNATIVE 2
Another alternative is to spawn another thread and do the work and sleep the remaining x minutes.
The advantage here is that if your function doesn't touch UI objects your application will remain responsive to user input while the function is being called
Private Sub Form_Load()
_timer.Enabled = True
End Sub
Private Sub _timer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
ListBox1.Items.Add(DateTime.Now.ToLongTimeString() + "," + DateTime.Now.ToLongDateString())
End Sub
i made an application for serial communication. for this application i need to set delay time. during this delay time i m doing some other task. So for those task i need to take back control from delay function, for this purpose i am unsing Doevents() function.Its work fine On other OS (XP, Windows7 32/64-bit). But Application.DoEvents() function halt and crash in windows vista.So is there any solution
Private Sub TimeDelay(ByVal DT As Integer)
Dim StartTick As Integer
StartTick = Environment.TickCount()
While ((Environment.TickCount() - StartTick) <= DT)
Application.DoEvents()
End While
'Application.DoEvents()
End Sub
thanks in advance
Try using a BackgroundWorker component instead of calling Application.DoEvents().
Please try System.Threading.Thread.SpinWait(10) after the Application.DoEvents, it might work.
I would recommend putting a "System.Threading.Thread.Sleep(1)" in the loop as well. It might happen because there are too many events pending for Windows to process, therefore ending in high CPU usage.
Sleeping 1 millisecond is very little (actually only 0,001 second). And it would decrease CPU usage dramatically as well, while still allowing the program to remain responsive.
The final code would be:
Private Sub TimeDelay(ByVal DT As Integer)
Dim StartTick As Integer
StartTick = Environment.TickCount()
While ((Environment.TickCount() - StartTick) <= DT)
Application.DoEvents()
System.Threading.Thread.Sleep(1)
End While
'Application.DoEvents()
End Sub
Try running this code:
TimeDelay(1000000)
You will notice that in the process, the program will consume almost 100% CPU with your code, but 0% with mine.
You shouldn't use DoEvents for this purpose.
Create a seperate thread to run the code you have provided. And use a call back (thread completed) to notify when the time has elapsed.
Imports System.Threading
Public Class Tester
Shared WithEvents oSquare As SquareClass = New SquareClass()
Public Shared Sub Main
Dim t As Thread
t = New Thread(AddressOf oSquare.TimeDelay)
t.Start()
End Sub
Shared Sub SquareEventHandler() Handles oSquare.ThreadComplete
Console.WriteLine("Completed")
End Sub
End Class
Public Class SquareClass
Public DT As Integer = 5000 ' 5 seconds (edited thanks to Mathias)
Public Event ThreadComplete()
Public Sub TimeDelay()
Dim StartTick As Integer
StartTick = Environment.TickCount()
While ((Environment.TickCount() - StartTick) <= DT)
thread.sleep(1000)
End While
RaiseEvent ThreadComplete()
End Sub
End Class