Timers and variables in vb.net - vb.net

I have a class with a timer. The timer starts in the new method. The method associated with the timer is checkMatch() but checkTimer() ensures the parameters are met to start the timer. They are met and the timer starts. The method checkMatch() is currently rewritten to only execute once since I know it keeps repeating itself. The only place to change the boolean variable oneTime is located inside of checkMatch(). I included the autoLoad() function just in case it is needed. Whenever I launch this class, the forms associated with the other methods not listed will pop up and instantly go away. The console shows that checkMatch() is being called regularly (which it should), but that the variable oneTime remains True each time it is called. The timer is started at the end of the autoLoad() method. I know all the methods in autoLoad() are being called and executed.
Since oneTime is showing as True constantly in the console, I am assuming I have a conception issue somewhere. Why is oneTime true each time checkMatch() is called?
' a user is required for this class
Public Sub New(ByVal my_user As RGOUser)
My.Settings.selected_summoner = "huge legend"
My.Settings.Region = "na1"
My.Settings.Save()
myUser = my_user
AddHandler tmr.Elapsed, AddressOf checkMatch
checkTimer()
End Sub
Public Sub checkMatch()
Console.WriteLine(Me.GetType.ToString() + "||" + System.Reflection.MethodInfo.GetCurrentMethod.ToString())
Console.WriteLine(oneTime)
Try
Dim psList() As Process = Process.GetProcesses()
For Each p As Process In psList
' If strLeagueProcessName = p.ProcessName) And Not boolInGame Then
' remove later and replace with commented line
If oneTime Then
Console.WriteLine("checkMatch Success.")
boolInGame = True
oneTime = False
tmr.Interval = timerInsideMatch
tmr.Stop()
Try
autoLoad()
Catch ex As Exception
End Try
Return
Else
Return
End If
Next
Catch ex As Exception
Console.WriteLine("checkMatch Failure. " + ex.Message)
End Try
boolInGame = False
tmr.Interval = timerOutsideMatch
End Sub
Private Sub autoLoad()
Console.WriteLine(Me.GetType.ToString() + "||" + System.Reflection.MethodInfo.GetCurrentMethod.ToString())
If searchMatch() Then
Dim x As New System.Threading.Thread(AddressOf loadConfiguration)
Dim y As New System.Threading.Thread(AddressOf loadMatch)
Dim z As New System.Threading.Thread(AddressOf launchMinimap)
If My.Settings.configuration_auto_load Then
Try
x.Start()
Catch ex As Exception
End Try
End If
If My.Settings.loadMatch Then
Try
y.Start()
Catch ex As Exception
End Try
End If
If My.Settings.launchMinimap Then
Try
z.Start()
Catch ex As Exception
End Try
End If
x.Join()
y.Join()
z.Join()
tmr.Start()
End If
End Sub

Related

Async reading from process gets paused

I have made an application in vb.net which launches a cmd like process in the background, reads its output asynchronously and redirects it in a textbox. Normally my application should show every new output line in the textbox in real time. Unfortunately here is what happens:
These are the first output lines which are shown in the textbox.
After a few seconds an unwanted pause occurs for some reason. No new lines are shown in the textbox even though there is output from the process. After a few minutes all that output i couldn't see because of the pause, shows up and then a pause occurs again.
Finally, the last bunch of lines show up and the process is terminated.
Here is my code:
Private Sub StartSteamCMD()
Try
Dim psi As ProcessStartInfo = New ProcessStartInfo("cmd.exe", "/C steamcmd +runscript " + temp + "Setti_SRCDS_Manager\install_update.txt")
With psi
.UseShellExecute = False
.RedirectStandardInput = True
.RedirectStandardOutput = True
.RedirectStandardError = True
.CreateNoWindow = True
.StandardOutputEncoding = Encoding.GetEncoding(CultureInfo.CurrentUICulture.TextInfo.OEMCodePage)
.StandardErrorEncoding = Encoding.GetEncoding(CultureInfo.CurrentUICulture.TextInfo.OEMCodePage)
End With
process = New Process With {.StartInfo = psi}
AddHandler process.OutputDataReceived, AddressOf Async_Data_Received
AddHandler process.ErrorDataReceived, AddressOf Async_Data_Received
process.Start()
process.BeginOutputReadLine()
process.BeginErrorReadLine()
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub Async_Data_Received(sender As Object, e As DataReceivedEventArgs)
Try
Invoke(New InvokeWithString(AddressOf Sync_Output), e.Data)
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub Sync_Output(text As String)
Try
TextBox1.AppendText(text + Environment.NewLine)
TextBox1.ScrollToCaret()
If Not String.IsNullOrEmpty(text) Then
If text.Contains("downloading") Or text.Contains("validating") Then
<code to animate the progress bar here>
ElseIf text.Contains("Success") Then
<the server is installed successfully, some code to run here>
ElseIf text.IndexOf("error", 0, StringComparison.CurrentCultureIgnoreCase) > -1 Then
<some code to run in case of error>
End If
End If
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Any thoughts of what can cause those pauses? I have been thinking the following. As you can see in the first screenshot, the last two lines are about establishing a connection with Steam servers (the place where the game server files will be downloaded from) and waiting for user info. Maybe that moment the process doesn't produce any output for a few seconds. So at some point during those seconds, the first pause of async reading occurs. Then the process starts producing output again but it's not redirected to the textbox because of the pause. Could it be the async reading gets paused because of the process not producing any output for a few seconds?
I don't know what else to think. I'm tired of searching the internet for a solution, because i can't find any post about my problem or at least something similar. I hope you guys can help me out. Thanks in advance

How do I call a function on the main thread from another thread in vb

I have a timer function which is called on my windows form which works perfectly when a button on the UI is clicked. However, in my application Im connecting to a server using a TCP socket, when the server disconnects Im catching the exception thrown and Id like at this point for the timer to start (ticker) then run the reconnection.
When i reference the timer from the try catch it wont run, so I imagine its because its on the main thread?
Here is my code for the timer:
Public Delegate Sub DroppedConnectionDelegate(ByVal ReConn As Integer)
Public Sub DroppedConnection(ByVal ReConn As Integer)
Console.Write("Dropped Connection()")
Thread.Sleep(1000)
If ReConn = 1 Then
MakeConnection(My.Settings.savedIP, False)
Else
isRunning = False
Second = 0
'client.Close()
'client.Dispose()
Timer1.Interval = 1000
Timer1.Enabled = True
Timer1.Start() 'Timer starts functioning
End If
' End If
End Sub
Public Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
Second = Second + 1
Console.WriteLine(Second)
If Second >= 10 Then
Timer1.Stop() 'Timer stops functioning
Timer1.Enabled = False
MakeConnection(ServerAddressString, False)
End If
End Sub
Here is the sub from where Id like to call the timer on catch exception:
Public Shared Sub ReceiveCallback(ByVal ar As IAsyncResult)
Dim state As StateObject = CType(ar.AsyncState, StateObject)
Dim client As Socket = state.workSocket
Dim strReceiveBuffer As String = String.Empty
' Read data from the remote device.
Dim bytesRead As Integer
Console.WriteLine("ar to string = " & ar.ToString)
'While client.Connected
If (AsynchronousClient.connectDone.WaitOne(5000, True)) Then
Try
If client.Connected = True Then
bytesRead = client.EndReceive(ar)
Console.WriteLine("Socket Connected")
If bytesRead > 0 Then
state.sb.Clear()
' There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead))
strReceiveBuffer = state.sb.ToString()
MainForm.main_Response = state.sb.ToString()
If strReceiveBuffer.IndexOf("doses-remaining") > 0 Then
response = state.sb.ToString()
MainForm.GetLabelTextToString(response)
'receiveDone.Set()
'isReceived = True
'Return
End If
' Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReceiveCallback), state)
Else
' All the data has arrived; put it in response.
If state.sb.Length > 1 Then
response = state.sb.ToString()
End If
' Signal that all bytes have been received.
receiveDone.Set()
isReceived = True
End If
Else
MainForm.WriteLog("RecieveCallback() Error outside catch :" & Date.Today.ToString & " " & TimeOfDay)
MainForm.UpdateList("RecievecallBack error, attempting reconnect..." & client.RemoteEndPoint.ToString())
MainForm.isRunning = False
MainForm.DroppedConnection(0)
End If
Catch ex As Exception
'MessageBox.Show("ReceiveCallback Error, Check Log.")
MainForm.WriteLog("RecieveCallback() Error inside catch :" & Date.Today.ToString & " " & TimeOfDay & ex.Message)
MainForm.UpdateList("RecievecallBack error, attempting reconnect..." & client.RemoteEndPoint.ToString())
client.Shutdown(SocketShutdown.Both)
client.Close()
client.Dispose()
MainForm.isRunning = False
Dim d As DroppedConnectionDelegate = AddressOf MainForm.DroppedConnection
'MainForm.DroppedConnection(0)
d.BeginInvoke(0, Nothing, Nothing)
Exit Try
End Try
End If
' End While
End Sub 'ReceiveCallback
If I run the application and the server disconnects unexpectedly, currently it wont reconnect automatically. But it will run the timer if I click the connect button (which calls the timer anyway)
Can anyone help please?
Thanks in advance
I'm not 100% sure as I've never tried but I think that if you use a Timers.Timer instead of a Windows.Forms.Timer then what you're doing now will work. By default, a Timers.Timer will raise its Elapsed event on a secondary thread but you can assign a form or other control to its SynchronizingObject property to make it raise events on the UI thread. Calling Start from a secondary thread should be OK in that case.
If that doesn't work then you can do what you want by first marshalling a method call to the UI thread before starting the Timer. That might look like this:
Private Sub StartTimer()
If Me.InvokeRequired Then
Me.Invoke(New Action(AddressOf StartTimer))
Else
Me.Timer1.Start()
End If
End Sub
You can call that method on any thread and it will just work.
Note that setting the Enabled property and calling Start or Stop is redundant. Start already sets Enabled to True and Stop sets it to False. Do one or the other, not both. It is most appropriate to call a method when you know for a fact that you want to either start or stop the Timer. If you have a Boolean value that might be either True or False then you should use the property. Setting the property using a literal value is not wrong per se but it is not what was intended. An example of where it's appropriate to use the property is when you use the same method to both start and stop the Timer from a secondary thread:
Private Sub EnableTimer(enable As Boolean)
If Me.InvokeRequired Then
Me.Invoke(New Action(Of Boolean)(AddressOf EnableTimer), enable)
Else
Me.Timer1.Enabled = enable
End If
End Sub
Again, calling that method on any thread will just work.

Exception handling in TPL

I am using the following code for creating some files.
I have observed that sometimes if some exception occurs all Parallel.For threads stop in between.
I have few questions.
Should I be using AggregateException in CreateReport method or its # rt place.
How to make sure that if exceptions arises in any of the threads it does not stop other parallel threads.
Try
dtScheduledReports = objReprotHelper.GetTopImmediateReportsForExecution()
Parallel.For(0, dtScheduledReports.Rows.Count, Sub(i)
CreateReport(dtScheduledReports.Rows(i))
End Sub)
Catch ae As AggregateException
For Each ex As Exception In ae.InnerExceptions
ExceptionHandler.LogError(ex)
Next
End Try
Private Sub CreateReport(dtRow As DataRow, scheduleType As Integer)
Try
//do something
Catch
throw
End Try
End Sub
You can use a ConcurrentQueue(Of Exception) to enable safe exception enqueueing from multiple threads. This allows to execute the entire loop and throws all exceptions in an AggregateException.
Private Sub DoAParalelJobAndThrowErrors
Dim exQueue as New ConcurrentQueue(Of Exception)
dtScheduledReports = objReprotHelper.GetTopImmediateReportsForExecution()
// Execute the complete loop and capture all exceptions.
Parallel.For(0, dtScheduledReports.Rows.Count, Sub(i)
Try
CreateReport(dtScheduledReports.Rows(i))
Catch ex as Exception
exQueue.Enqueue(ex)
End Try
End Sub)
If exQueue.count > 0 throw new AggregateException(exQueue)
End Sub
Private Sub CreateReport(dtRow As DataRow, scheduleType As Integer)
//do something
End Sub
According MSDN this way doen't break the loop, all rows should be processed.

How to handle errors in a cross thread operation

I have a webbrowser control created at runtime and used via a background thread. The following is an example of the code used:
If Me.InvokeRequired Then Me.Invoke(Sub() webbroswers(3).Document.GetElementById("ID").InnerText = TextBox4.Text)
This works great! But sometime the webbrowser doesn't have the element "ID" (for example). So I'd like a method to basically allow the code to continue if an error occurs. I've tried the try - catch block but this doesn't catch it!
You can use a multi-line lambda expression, like this:
If Me.InvokeRequired Then Me.Invoke(
Sub()
Dim element As HtmlElement = webbroswers(3).Document.GetElementById("ID")
If element IsNot Nothing Then
element.InnerText = TextBox4.Text
End If
End Sub
)
Checking if it's Nothing, like that, is more efficient than letting it fail and catching the exception. However, if you need to do a Try/Catch for any other reason, you can also do that easily in a multi-line lambda expression, for instance:
If Me.InvokeRequired Then Me.Invoke(
Sub()
Try
webbroswers(3).Document.GetElementById("ID").InnerText = TextBox4.Text
Catch ex As Exception
' ...
End Try
End Sub
)
However, if the lambda expression gets too long, or if you'd like to have more meaningful stack traces in your exceptions, you can use a delegate to an actual method, like this:
If Me.InvokeRequired Then Me.Invoke(AddressOf UpdateId)
'...
Private Sub UpdateId()
Try
webbroswers(3).Document.GetElementById("ID").InnerText = TextBox4.Text
Catch ex As Exception
' ...
End Try
End Sub

Is it correct to raise custom events from Timer/ BackgroundWorker Completed that go on and update UI

My winforms application needs to interact with hardware devices.
The display is kind of workflow... after step 1 .. completed.. go to step 2 .. etc.
The display is dynamic with controls being made visible at run time and activity being initiated (each of these User controls use timer/ BGWorker within).
I m Raising custom events from timer/ BGWorker_Completed. this will help proceed to the next step and update UI. Am I doing the right thing?
Im a newbie in winforms and Im just not able to figure out why the display is failing.
Im not catching any exception... but after a particular step I do not see the controls !!! I do not know where and how to debug this scenario. If I execute the main form as a stand alone.. I can see the display as well.
however if I navigate from the login page/ change the tabs within the main form and return back.. I do not see the display.
I tried putting in checks before calling the update on UI and in the same order as below.
Thread.Current.IsBackground returns false, control.IsHandleCreated returns true or else Im creating it with dim x=Control.handle()) Me.InvokeRequired/ Control.invokeRequired returns false (the way I wanted). However I do not see userControl being displayed... visibility/color/ everything is being set (in the program).. and Im able to see the hardware interaction!!!.. But no display :-( (step 4 and later)
Im not doing anything fancy in the login page...or in tabChanged events.
(On tab changed event... Im only cleaning up open connections/ closing bg workers if any.. which would be connected back whenever required)
Please let me know if I need to do anything...and how to solve this problem.
I m also calling EnsureHandleIsCreated(control) subroutine soon after initialize component of every user control/ main form.
'Code in Login Form
Dim myForm as new MainForm()
myForm.ShowDialog(Me) ' here i also tried with show/showDialog.. with/without ownerForm
Me.Hide() ' Hide login page
'Code for checking if handle is created or not
Public Sub CheckForInvalidThread()
frmMain.CheckForIllegalCrossThreadCalls() = True
If Thread.CurrentThread.IsBackground Or Not Thread.CurrentThread.Name Is THREAD_MAIN_NAME Then
Throw New InvalidOperationException(THREAD_IS_INVALID)
End If
If Not Me.IsHandleCreated Then
Dim x = Me.Handle()
Thread.Sleep(20)
End If
End Sub
Public Sub EnsureHandleIsCreated(ByRef c As Control)
Try
If Not c.IsHandleCreated Then
Dim h As System.IntPtr = c.Handle()
End If
If c.HasChildren Then
For Each child As Control In c.Controls
Try
EnsureHandleIsCreated(child)
Catch ex As Exception
DAL.LogException(ex.Message, ex.StackTrace, "EnsureHandleIsCreated: " & c.Name, 0)
End Try
Next
End If
Catch ex As Exception
DAL.LogException(ex.Message, ex.StackTrace, "EnsureHandleIsCreated: " & c.Name, 0)
End Try
End Sub
Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
lbRole.Text = RoleName
lbName.Text = UserName
Try
Me.Activate()
If Thread.CurrentThread.IsBackground Then
Throw New ApplicationException(THREAD_IS_INVALID)
End If
Thread.CurrentThread.Name = THREAD_MAIN_NAME
CheckForInvalidThread()
clGlobals.frmMain = Me
If RoleName Is Nothing Or String.IsNullOrEmpty(RoleName) Or RoleName.Equals("OPERATOR", StringComparison.InvariantCultureIgnoreCase) Then
tcEtch.TabPages("tbMaintenance").Hide()
tcEtch.TabPages("tbAdmin").Hide()
End If
Catch ex As Exception
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Exit Sub
Finally
End Try
Initiate()
End Sub
Public Sub GoToNextStep() Handles Me.GoToNextStepEvent
Try
CheckForInvalidThread()
CurrentStep = CurrentStep + 1
Select Case CurrentStep
Case 0 To 2
If Me.InvokeRequired Then
_delegateDisplayInitiate = AddressOf DisplayStep2
Me.Invoke(_delegateDisplayInitiate)
Else
DisplayStep2()
End If
Case 3
If ucCycleStart.InvokeRequired Then
_delegateDisplayInitiate = AddressOf DisplayStep3
ucCycleStart.Invoke(_delegateDisplayInitiate)
Else
DisplayStep3()
End If
Case 4
If Me.InvokeRequired Or ucPartCountVerification.InvokeRequired Or Thread.CurrentThread.IsBackground Then
Throw New Exception("Check out")
End If
EnsureHandleIsCreated(ucPartCountVerification)
If ucPartCountVerification.InvokeRequired Then
_delegateDisplayInitiate = AddressOf DisplayStep4
ucPartCountVerification.Invoke(_delegateDisplayInitiate)
Else
DisplayStep4()
End If
End Select
Catch ex As Exception
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
Private Sub DisplayStep4() Handles Me.DisplayStep4Event
ucPartCountVerification.Visible = True
ucPartCountVerification.Show()
ucPartCountVerification.Initiate()
End Sub
Public Sub Initiate()
frmMain.CheckForInvalidThread()
'Just to verify if things are fine.. i put in this check below
If Me.InvokeRequired Or pnStep4.InvokeRequired Or Not (Me.IsHandleCreated And pnStep4.IsHandleCreated ) Then
MessageBox.Show("cHECK OUT")
Else
Me.Visible = True
pnStep4.Visible = True
Me.BackColor = Color.Red
pnStep4.BackColor = Color.Gray
Dim height = Me.Size.Height
Dim width = Me.Size.Width
MessageBox.Show(height.ToString() + Me.InvokeRequired.ToString())
End If
end Sub
You certainly can raise events from a BackgroundWorker or Timer. You're already checking InvokeRequired, which is the correct thing to do. You need to then call BeginInvoke (or Invoke, if it needs to be synchronous) to update your UI.
You are on the right track. When dealing with the events that come from some libraries, you cannot be certain on which thread they are delivered. That is disappointing because Windows Forms has the restriction that call calls be made from the GUI thread. This MSDN article explains the problem.
You are on the right track with InvokeRequired. It lets you see whether you are on the right thread, but you need a way to handle this case and re-invkoe the event on the proper thread.
Here is how I do this in C#...
public delegate void uiEventHandler();
void uiEvent(object sender, EventArgs e)
{
if (InvokeRequired)
{
// We are not on the correct thread. We'd better get there.
var eh = new uiEventHandler(uiEvent);
Invoke(eh, new object[] { sender, e });
return; //This thread has no more work to do.
}
// Do your work here that requires being performed on the GUI thread.
}