While loop causes the app to go slow? Any idea why? - vb.net

I have a simple code that looks up a text file, reads the line of text, splits the string by semi-colons and then posts the results.
After it has done this, I have created a really simple while loop to waste 10 seconds before going for it again.... here is the code:
Private Sub checkTemps()
While Abort = False
Try
fileReader = New StreamReader(directory.Text & "currentTemp.dat")
rawData = fileReader.ReadLine()
fileReader.Close()
Dim dataArray() As String
dataArray = rawData.Split(";")
updateOutput("1", dataArray(0), dataArray(1))
updateOutput("2", dataArray(2), dataArray(3))
updateOutput("3", dataArray(4), dataArray(5))
updateOutput("4", dataArray(6), dataArray(7))
stpWatch.Start()
While stpWatch.Elapsed.Seconds < 10 And Abort = False
pollInterval(stpWatch.ElapsedMilliseconds)
End While
stpWatch.Stop()
stpWatch.Reset()
Catch ex As Exception
msgbox("oops!")
End Try
End While
closeOnAbort()
End Sub
But when it gets to the "time-wasting" loop - it seems to slow the whole application down? And I can't work out why!
So a couple of questions... is there a better way to do all this? and second - can anyone spot a problem?
All the other commands seem to run fine - there isn't much else to this app. I have another program that updates the dat file with the values, this is simply a client side app to output the temperatures.
Any help would be appreciated.
Andrew
More info:
I should explain what the pollInterval sub does!
Private Delegate Sub pollIntervalDelegate(ByVal value As Integer)
Private Sub pollInterval(ByVal value As Integer)
If Me.InvokeRequired Then
Dim upbd As New pollIntervalDelegate(AddressOf pollInterval)
Me.Invoke(upbd, New Object() {value})
Else
ProgressBar1.Value = value
End If
End Sub

Your loop is a very tight loop continually calling pollInterval. This will tie up the application until the loop condition is met.
You should use the Sleep method to pause this thread for the required amount of time.
If you want to show the progress (as per your update) you could put the Sleep into the loop and sleep for 1 second (or half a second?) at a time:
While stpWatch.Elapsed.Seconds < 10 And Abort = False
Sleep(1000) <-- NOT 100% sure of the syntax here,
but the time is specified in milliseconds
pollInterval(stpWatch.ElapsedMilliseconds)
End While

You should go with
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(10).TotalMilliseconds);

Related

Why is my invoke adding 10 seconds onto grid loading time?

I've attempted to add a method invoker to stop my error log being spammed with "Bounds cannot be changed while locked."
This has solved my issue, however...It has added an extra 10 seconds onto the loading time of my RadGridView.
I looked at https://www.telerik.com/forums/bounds-cannot-be-changed-while-locked to setup my invoker but there isn't much else that I can see to help with my issue.
I've attached a sample of my code below, any help would be appreciated.
Private Sub bgw_initialLoad_DoWork(sender As Object, e As DoWorkEventArgs)
Try
liveDS = New DataSet
Dim dsholder As DataSet = GetDataFromSQL("LoadData")
Dim dt1 As DataTable = dsholder.Tables(0)
Dim dt_1 As DataTable = dt1.Copy()
dt_1.TableName = "Customer"
liveDS.Tables.Add(dt_1)
Dim dt2 As DataTable = dsholder.Tables(1)
Dim dt_2 As DataTable = dt2.Copy()
dt_2.TableName = "Orders"
liveDS.Tables.Add(dt_2)
Dim dt3 As DataTable = dsholder.Tables(2)
Dim dt_3 As DataTable = dt3.Copy()
dt_3.TableName = "OrderLine"
liveDS.Tables.Add(dt_3)
If RadGridView.InvokeRequired Then
RadGridView.Invoke(New MethodInvoker(AddressOf SetupDataSources))
Else
SetupDataSources()
End If
Catch ex As Exception
sendCaughtError(ex)
End Try
End Sub
Private Sub SetupDataSources()
If liveDS.Tables.Count > 1 Then
RadGridView.DataSource = liveDS.Tables("Customer")
liveOrdersTemplate.DataSource = liveDS.Tables("Orders")
liveOrdersTemplate2.DataSource = liveDS.Tables("OrderLine")
End If
End Sub
Private Sub bgw_initialLoad_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs)
Try
RadGridView.DataSource = liveDS.Tables("Customer")
Dim template As New GridViewTemplate()
template.DataSource = liveDS.Tables("Orders")
RadGridView.MasterTemplate.Templates.Add(template)
Dim template2 As New GridViewTemplate()
template2.DataSource = liveDS.Tables("OrderLine")
RadGridView.Templates(0).Templates.Add(template2)
Dim relation As New GridViewRelation(RadGridView.MasterTemplate)
relation.ChildTemplate = template
relation.ParentColumnNames.Add("Invoice Customer")
relation.ChildColumnNames.Add("InvoiceCode")
RadGridView.Relations.Add(relation)
Dim relation2 As New GridViewRelation(RadGridView.Templates(0))
relation2.ChildTemplate = template2
relation2.ParentColumnNames.Add("OrderNo")
relation2.ChildColumnNames.Add("OrderNo")
RadGridView.Relations.Add(relation2)
FormatGrid()
SplitContainer2.Panel1.Enabled = True
SplitContainer1.Panel2.Enabled = True
refreshMainGrid()
HideLoadingGif()
Catch ex As Exception
sendCaughtError(ex)
End Try
End Sub
Debugging threads can be hard, trust me. This isn't a "real" answer, but a bunch of tips which may help - which is what I hope will happen.
There are dedicated windows in the debug menu which may help. I started with this webpage when I was wondering what was happening to my application and why it wasn't obvious why it was happening.
Also, while your parallel thread is running, it may "silent crash" if your IDE isn't set to pause on every crash, in which case it won't return a value but will just stay silent. Make sure at least these options are set:
And don't forget to show this window while debugging: (previous image showed Threads and Call stack instead, while they are good to have around while debugging it's the parallel stacks which I was going for)
One last thing: such a big delay may be database related. I'm not saying that it is, but you should be aware of the possibility.
Now the following isn't part of the answer per se, but is more of a friendly advice: put your invoke logic in SetupDataSources() instead, this way wherever it's called you'll be thread safe. Like this:
Private Sub SetupDataSources()
If RadGridView.InvokeRequired Then
RadGridView.Invoke(Sub() SetupDataSources())
End If
If liveDS.Tables.Count > 1 Then
RadGridView.DataSource = liveDS.Tables("Customer")
liveOrdersTemplate.DataSource = liveDS.Tables("Orders")
liveOrdersTemplate2.DataSource = liveDS.Tables("OrderLine")
End If
End Sub
Best of luck... you might need some ;)

Why won't my code Loop?

Sorry for the messy code :S
If CheckBox2.Checked = True Then
For i As Integer = 0 To 1 Step 0
If CheckBox1.Checked = True Then
If TextBox1.Text = lblCLickLImit.Text Then
Timer1.Stop()
TextBox1.Text = "0"
System.Windows.Forms.SendKeys.Send("{F5}")
System.Threading.Thread.Sleep(delaydelaytime)
System.Windows.Forms.SendKeys.Send("{ENTER}")
Else
If CheckBox1.Checked = False Then
If TextBox1.Text = lblCLickLImit.Text Then
Timer1.Stop()
TextBox1.Text = "0"
End If
End If
End If
Else
If CheckBox2.Checked = False Then
If CheckBox1.Checked Then
If TextBox1.Text = lblCLickLImit.Text Then
Timer1.Stop()
TextBox1.Text = "0"
System.Windows.Forms.SendKeys.Send("{F5}")
System.Threading.Thread.Sleep(delaydelaytime)
System.Windows.Forms.SendKeys.Send("{ENTER}")
End If
Else
If CheckBox1.Checked = False Then
If TextBox1.Text = lblCLickLImit.Text Then
Timer1.Stop()
TextBox1.Text = "0"
End If
End If
End If
End If
End If
Next
Basically this code is for an Auto Clicker program,(Hopefully this will help you understand, http://prntscr.com/7tuc3o interface) Ok so when the "Continuous" checkbox is selected the code is in theory supposed to loop for infinity. However when I run the program with everything selected as shown all that happens is the program clicks once and then crashes (not responding). Any help I have tried this loop in other programs and it works, just not with this code.
Your loop is tying up the UI thread. You'll need to look into using either a background worker:
BackgroundWorker handles long-running tasks. It does not freeze the entire program as this task executes.
(dotnetperls.com)
Here is the msdn walkthrough of how to set-up a backgroundworker:
https://msdn.microsoft.com/en-us/library/ywkkz4s1.aspx
Or
If this is a personal project and no one you love will need to maintain this code, you can use Application.DoEvents() to continue to pump messages while the program is looping. Here is the msdn documentation for that https://msdn.microsoft.com/en-us/library/system.windows.forms.application.doevents(v=vs.110).aspx
First of all, a Step of 0 doesn't really make any sense in a for loop. It may /work/ but it is going to drive anyone reading it later insane. If you want an infinite loop don't use a for loop, use:
While True
'code here
End While
For loops are used when you know exactly how many iterations of your loop you need. A while loop is designed to iterate as long as some condition is true. In this example the condition is always true so it loops indefinitely.
Your code is also just going to spin the UI thread constantly. It never pauses for input (even your sleep calls do not release the thread for input). As far as the OS knows, your app has locked up because it never processes any of the window messages that get posted to it. It is still happily spinning away though, until windows finally gets tired of it and prompts you to kill it.
Not sure what other "programs" you're working with but I can tell you you don't want to use a For loop for this. You want a do/while loop, like either:
While True
...
End While
or
Do
...
Loop While True

how to use Application.OnTime to delay a loop

SO I'm writing a code which makes references to bloomberg cell formulas... as a result the loop often skips faster than data can load. I think this can be solved relatively easily if I can somehow delay the iteration of the loop ... which would give cells time to populate. This is what I have written so far, and I'm not sure how to write the last line really.
x = 1
If x < TixCollection.count Then
' runs report if ADR Close is active
If Sheets("Input").Range("L2").value = "x" Then
Call build_singleEquity(x)
'Set pat = New pattern
Application.OnTime Now + TimeValue("00:00:10"), "pattern_recogADR"
If Sheets("Input").Range("L5").value = "x" Then
Dim sht_name As String
sht_name = TixCollection(x).ADR & "_ADRclose"
Call Sheet_SaveAs(path_ADRclose, sht_name, "SingleEquityHistoryHedge")
End If
End If
'runs report if ORD Close is active
If Sheets("Input").Range("L9").value = "x" Then
Call build_ordCloseTrade(x)
If Sheets("Input").Range("L13").value = "x" Then
Dim sht_name1 As String
sht_name1 = TixCollection(x).ADR & "_ORDclose"
Call Sheet_SaveAs(path_ORDclose, sht_name1, "OrdCloseTrade")
End If
End If
Application.OnTime Now + TimeValue("00:00:15"), x = x + 1 'want to write something like this but syntax is wrong
Application.OnTime essentially schedules an event to run at/after a particular time. It allows you to schedule an event to run even after your code has executed and cleared the stack. This is why it's an Application level method.
The OnTime method doesn't pause or delay anything, it's simply a scheduler. So the rest of your code will continue to execute, and to whatever extent that code relies on the results of the task that is waiting for the OnTime, you'll run in to errors.
In theory, I think you could probably make this approach work in a roundabout way for your purposes, but I think you'd probably be better served to use the WinAPI Sleep function. This also gives you greater granularity (you can specify to the millisecond).
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
You can invoke this from any subroutine or function, by calling:
Sleep 5000 'pause for 5000ms, (5 seconds)
UPDATE
I notice your code doesn't actually contain a Loop structure. I think I understand what you're trying to do. Here's a stab at it:
Do While x < TixCollection.count Then `or consider: Do While x <= TixCollection.Count
' runs report if ADR Close is active
If Sheets("Input").Range("L2").value = "x" Then
Call build_singleEquity(x)
'Set pat = New pattern
Sleep 10000
Call pattern_recogADR
If Sheets("Input").Range("L5").value = "x" Then
Dim sht_name As String
sht_name = TixCollection(x).ADR & "_ADRclose"
Call Sheet_SaveAs(path_ADRclose, sht_name, "SingleEquityHistoryHedge")
End If
End If
'runs report if ORD Close is active
If Sheets("Input").Range("L9").value = "x" Then
Call build_ordCloseTrade(x)
If Sheets("Input").Range("L13").value = "x" Then
Dim sht_name1 As String
sht_name1 = TixCollection(x).ADR & "_ORDclose"
Call Sheet_SaveAs(path_ORDclose, sht_name1, "OrdCloseTrade")
End If
End If
x = x+1
Sleep 15000 'Wait for 15 seconds
Loop
It is hard to tell if both of the Sleep (your previous OnTime calls) are really needed, since I'm not sure which one (or if both) was introducing the error condition.
You will need to make sure that you put the code for the Sleep function in the vba project.
Update
Assuming the sleep function, or Application.Wait method does not work for you, one other thing you could try is a simple Do/While Loop. Although I am not able to replicate your condition, this seems like possibly the most reliable.
Dim newTime As Date
newTime = Now + TimeValue("00:00:10")
Do While Not Now >= newTime
DoEvents
Loop
A final option would be to disable and manually force calculation, like below. My understanding is that the application is busy and will not execute code while a calculation event is occurring. However, with this in mind I'm not sure if any of these approaches will work for you, because although you indicate it's waiting on an Excel worksheet calculation, I don't think that is possible, the worksheet event takes precedence over running code, so I'm thinking that something is still happening on the client side which you might not be able to trap reliably unless they provide some sort of method through the API (something like .Busy which returns a boolean, etc.).
Dim appCalc as Long: appCalc = Application.Calculation
Application.Calculation = appCalc '# disable automatic calculation
Call build_singleEquity(x)
Application.Calculate 'Force calculation
Application.Calculation = xlCalculationAutomatic '# return to normal/previous property
Call pattern_recogAD

Detect when exe is started vb.net

Dose anybody know how I can make my VB.net application wait until a process is detected as running?
I can find example of how to detect once an exe has finished running but none that detect when an exe is started?
You can use the System.Management.ManagementEventWatcher to wait for certain WMI events to occur. You need to give it a query type and condition to have it watch for the next creation of your process, then get it to do something when that occurs.
For example, if you want :
Dim watcher As ManagementEventWatcher
Public Sub Main()
Dim monitoredProcess = "Notepad.exe"
Dim query As WqlEventQuery = New WqlEventQuery("__InstanceCreationEvent", new TimeSpan(0, 0, 1), "TargetInstance isa ""Win32_Process"" And TargetInstance.Name = """ & monitoredProcess & """")
watcher = New ManagementEventWatcher()
watcher.Query = query
'This starts watching asynchronously, triggering EventArrived events every time a new event comes in.
'You can do synchronous watching via the WaitForNextEvent() method
watcher.Start()
End Sub
Private Sub Watcher_EventArrived(sender As Object, e As EventArrivedEventArgs) Handles watcher.EventArrived
'Do stuff with the startup event
End Sub
Eventually you'll need to stop the watcher, which is you can do by closing the app, or calling watcher.Stop(). This has been written as brain compiler, so if there's any issues let me know.
You could simply wait and check every once in a while whether the process exists. Use Thread.Sleep to avoid busy waiting.
However, this has the possibility that you miss the process if it starts and exists during your wait time.
You can use the below condition
return Process.GetProcesses().Any(Function(p) p.Name.Contains(myProcessName))
Dim p() As Process
Private Sub CheckIfRunning()
p = Process.GetProcessesByName("processName")
If p.Count > 0 Then
' Process is running
Else
' Process is not running
End If
End Sub
OR SIMPLY
System.Diagnostics.Process.GetProcessesByName("processName")

Auto-restart and then continue the Sub in VB.NET

Well I have some subs like:
Private Sub somesub()
'Processses that reach 900 mb in 1 hour and an half
End Sub
I want to restart the app, dispose memory and then return to where I was.
Exactly, I have an app that adds contacts and well It reach 900mb when 2000 contacts are added... I want to stop every 200 contacts, and do that I have said, and the code that I have no tested:
Imports SKYPE4COMLib
Public Class frmMain
Dim pUser As SKYPE4COMLib.User
Dim contactos As Integer
If contactos < 200 Then
For Each oUser In ListBox1.Items
pUser = oSkype.User(oUser)
pUser.BuddyStatus = SKYPE4COMLib.TBuddyStatus.budPendingAuthorization
oSkype.Friends.Add(pUser)
contactos += 1
Next
Else
'System.Windows.Forms.Application.Restart()
'I need a code that continues where I was, here...
End If
End Sub
End Class
What can I do? Thanks!
I have written some code below that may solve your problem. It certainly should save your position to a file and then when the file runs again, it would reload that position.
A couple of points.
I moved your declaration of pUser, and set it to nothing when I was done. This way the object is marked for disposal immediately.. you might get more than 200 revolutions out of this with that structure change.. but it might be a tad slower.
You will need some sort of reload for your listbox. I assume that you did not include it as part of your sample for brevity.
I changed your foreach loop to a for loop structure. This allows you to track your position within the list .. as a result I had to create a new oUser since you were iterating that in the foreach and did not list its type, you will need to fix that part of the code.
Obviously I have not compiled the below code, but it should give you a decent start on what your trying to do.
Be careful of the Process.Start, as you can set the current process to start another process and wait for that process to exit before exiting the current one and that would be very very very very bad and really cause an OutOfMemoryException quickly. You need to let the current process start the next instance and then without checking to see if it was successful at starting it .. exit. Or if you have used the restart command in your comments use that. The process spawning method might do what your wanting more effectively because your starting a new process on the computer and letting the old one be garbage collected (thus releasing the resources it was using.
Imports SKYPE4COMLib
Public Class frmMain
'Put code here to load position from the file
Dim startingPosition as Integer = 0
If IO.File.Exists("c:\filename.txt")
Using sr as New IO.StreamReader("c:\filename.txt")
sr.Read
StartingPosition = Convert.ToInteger(sr.ReadToEnd)
sr.Close
End Using
End If
'Probably needs some code somewhere to reload your listbox
Dim contactos As Integer
Dim CurrentPosition as Integer = 0
If contactos < 200 and StartingPosition < ListBox1.Items.Count Then
For x as integer = StartingPosition to ListBox1.Items.Count - 1
Dim oUser as <YOURTYPEHERE> = Ctype(ListBox1.Items(x), <YOURTYPEHERE>)
Dim pUser As SKYPE4COMLib.User
pUser = oSkype.User(oUser)
pUser.BuddyStatus = SKYPE4COMLib.TBuddyStatus.budPendingAuthorization
oSkype.Friends.Add(pUser)
contactos += 1
pUser = Nothing 'set the garbage collection to collect this.
CurrentPosition = x
Next
Else
'Save Your place to an external File, all your doing here is opening a file
'and saving the index of where you are in the listbox.
Using sw as New IO.StreamWriter("c:\filename.txt")
sw.Write(CurrentPosition)
sw.Close
End Using
'use the process namespace to have this app start the next copy of your app
'be careful not to use WaitForExit or you will have two copies in memory...
Process.Start("exename")
'or if the command below exists.. use that.. I never have.
'System.Windows.Forms.Application.Restart()
'I need a code that continues where I was, here...
End If
End Sub
End Class