Application Ends when TimerCallback function executes - vb.net

I was trying to create a very basic scheduler app in VB.NET (4.0). There is a form and button and label. So what exactly I want to happen is after 10 seconds the label text has to be changed. So here is the stuffs I did.
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim t As Timer
t = New Timer(New TimerCallback(AddressOf timerevent))
Dim scheduledTime As DateTime = DateTime.MinValue
scheduledTime = DateAdd(DateInterval.Second, 10, DateTime.Now)
Dim timespan As TimeSpan = scheduledTime.Subtract(DateTime.Now)
Dim dueTime As Integer = Convert.ToInt32(timespan.TotalMilliseconds)
t.Change(dueTime, Timeout.Infinite)
End Sub
Private Sub timerevent(e As Object)
Try
Label1.Text = Now.ToString
Catch ex As Exception
Label1.Text = ex.Message
End Try
End Sub
But the issue is timerevent fires correctly on 10 seconds, but immediately the application stops execution or ends up. I'm not getting an idea why this happen.

You're using the System.Threading.Timer which will fire the callback in ThreadPool thread.
Inside the callback you're updating the UI --which will result in InvalidOperationException. Agreed; you have a try/catch which will catch the exception but you again do the same mistake in catch block. Nobody can save you there.
You simply need to use System.Windows.Forms.Timer to fix the problem. This will work because the winforms Timer will fire the Tick event in UI thread.
Know the difference between Timers in .Net framework.

Related

Command line arguments in VB.net Windows Form

I've confusing with using command line argument in VB.net Winform.
I'm created an application integrated with dlsrbooth. Dlsrbooth send a trigger via command line argument. (https://support.lumasoft.co/hc/en-us/articles/360000637854-Triggers-Webhooks-and-API)
dlsrbooth will send trigger 'session_end' at first time running and change trigger after we start to take a photo session.
Since dlsrbooth screen based trigger (when take picture, will be send another trigger) so I use timer to read the argument.
If I put check argument at main form (form 1), I can read each trigger. But if I put read argument at 4th form, I didn't receive next screen trigger, only first time running trigger (session_end trigger).
Imports System.Reflection.Emit
Public Class Form6
Private TargetDT As DateTime
Private TargetDT2 As DateTime
Private CountDownFrom As TimeSpan = TimeSpan.FromSeconds(60)
Private startFrom As TimeSpan = TimeSpan.FromSeconds(10)
Dim wait As TimeSpan
Public Sub checkstatus()
If (Val(Form6.Label1.Text) <= 0) Then
Try
Dim sCmdLine As String() = Environment.GetCommandLineArgs()
Using sw As New StreamWriter(strFile)
sw.WriteLine(sCmdLine(1))
sw.Flush()
End Using
Catch
End Try
End If
End Sub
Private Sub tmrCountdown_Tick(sender As Object, e As EventArgs) Handles tmrCountdown.Tick
Dim ts As TimeSpan = TargetDT.Subtract(DateTime.Now)
wait = TargetDT2.Subtract(DateTime.Now)
checkstatus()
Label1.Text = wait.Seconds.ToString
If ts.TotalMilliseconds > 0 Then
lbltimer.Text = ts.ToString("mm\:ss")
Else
lbltimer.Text = "00:00"
tmrCountdown.Stop()
Form1.Show()
Me.Close()
End If
End Sub
Private Sub Form6_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Width = lbltimer.Width
Height = lbltimer.Height
tmrCountdown.Interval = 1000
TargetDT = DateTime.Now.Add(CountDownFrom)
TargetDT2 = DateTime.Now.Add(startFrom)
tmrCountdown.Start()
End Sub
End Class
I already try put checkstatus in main form (form 1) and call it within timer in 4th form, but the result is same, can't read next screen trigger.
Is it possible to read command line argument on other form (not main form)? How? Or where form event that should I put the command line argument?
Note: I want to send "session_start" command via API but it's not working, so I'm delaying read trigger and read each second using timer.

Timer not starting in vb.net

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.

Console APP with Timer not running

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

Console Application with Timers and the Skype API

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

ProgressBar freezes when using multithreading

I have a ProgressBar that uses the marquee style when a report is being generated. The reason I am doing this is because the ReportViewer control I use takes some time to generate the report thus making the form unresponsive. I generate the report using a thread so the ProgressBar can show that the program is working. However, when I start the thread the ProgressBar freezes. I have already tried the BackgroundWorker but that didn't work so I used my own threading.
The reason I use the Invoke() method is because I can't make changes to the ReportViewer control on the thread I created because it was created on the UI thread.
The method that takes the most time processing is the RefreshReport() method of the ReportViewer control which is why I'm trying to do that on its own thread instead of the UI thread.
Any help would be appreciated. Thanks.
Here is the code for my thread variable:
Private t As New Thread(New ParameterizedThreadStart(AddressOf GenerateReport))
Here is the code for the button that generates the report:
Private Sub btnGenerateReport_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGenerateReport.Click
pbReports.Style = ProgressBarStyle.Marquee
If t.ThreadState = ThreadState.Unstarted Then
t.IsBackground = True
t.Start(ReportType.Roads)
ElseIf t.ThreadState = ThreadState.Stopped Then
t = Nothing
t = New Thread(New ParameterizedThreadStart(AddressOf GenerateReport))
t.IsBackground = True
t.Start(ReportType.Roads)
End If
End Sub
Here is the code that generates the report:
Public Sub GenerateReport(ByVal rt As ReportType)
If rvReport.InvokeRequired Then
Dim d As New GenerateReportCallBack(AddressOf GenerateReport)
Me.Invoke(d, New Object() {rt})
Else
rvReport.ProcessingMode = ProcessingMode.Remote
rvReport.ShowParameterPrompts = False
rvReport.ServerReport.ReportServerUrl = New Uri("My_Report_Server_URL")
rvReport.ServerReport.ReportPath = "My_Report_Path"
rvReport.BackColor = Color.White
rvReport.RefreshReport()
End If
If pbReports.InvokeRequired Then
Dim d As New StopProgressBarCallBack(AddressOf StopProgressBar)
Me.Invoke(d)
Else
StopProgressBar()
End If
End Sub
Your code is starting a new thread from the UI thread. The new thread then immediately marshals back to the UI thread using Invoke - so basically it's as if you hadn't made it multithreaded at all.
Instead of that, make the new thread do all the background processing it can and only marshal back to the UI for parts of the process that need to update the UI.
You can try using the ThreadPool to generate a new worker thread. I use the below in a WPF application to show a loading screen for anything that takes over 4 seconds or so.
You might need to change some of the syntax as I'm a C# guy...
Private Sub btnGenerateReport_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGenerateReport.Click
pbReports.Style = ProgressBarStyle.Marquee
ThreadPool.QueueUserWorkItem(Function(th) Do
GenerateReport(ReportType.Roads)
Dispatcher.BeginInvoke(DispatcherPriority.Normal, DirectCast(Function() Do
StopProgressBar()
End Function, Action)
End Function)
End Sub
Also, I believe that the Dispatcher.BeginInvoke is only in WPF and not in WinForms, so you my need to change that back to Me.Invoke or something.