This BackgroundWorker is currently busy and cannot run multiple tasks concurrently - vb.net

I am confused. Yes i understand I can't use the same backgroundworker to do two tasks at the same time. What I do not understand is this. Here is my code (all this thing does is set the marqueeanimationspeed of a progress bar...
'THE FOLLOWING SUB TOGGLES THE PROGRESS BAR
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
'CHECK THE STATE OF THE PROGRESS BAR AND TOGGLE IT
If ToolStripProgressBar1.MarqueeAnimationSpeed = 0 Then
ToolStripProgressBar1.MarqueeAnimationSpeed = 22
End If
ToolStripProgressBar1.MarqueeAnimationSpeed = 0
End Sub
OK, so how long can this possibly take? Doesn't the worker do the task and exit? So I put in a pause (system.threading.thread.sleep(2000)... same problem, made it 20 seconds... same problem.
So I am assuming this is a simple thing I'm missing, but I've spent more than an hour searching and I don't get it.
All I am trying to accomplish here is to start the marquee progress bar while the UI is running something else, and then stop it. I assume I can create another backgroundworker and just use it, but I want to understand why the first one is not done with the task.
Thanks, and again, yes I spent an hour searching and I find all kinds of "solutions" but no explanation as to why this thing is not finished.
OK SO HERE IS THE SUB CALLING THE BGW
'THE FOLLOWING SUB FIRES THE SETTING CONNECTION STRINGS SUB
Private Sub SetCSButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SetCSButton.Click
'START THE PROGRESS BAR & CHANGE THE LABEL
BackgroundWorker1.RunWorkerAsync()
Threading.Thread.Sleep(1000)
ToolStripStatusLabel1.Text = "Preparing the connection strings..."
Me.Refresh()
thread3 = New System.Threading.Thread(AddressOf SetConnectionStrings)
thread3.Start()
'STOP THE PROGRESS BAR & CHANGE THE LABEL
BackgroundWorker2.RunWorkerAsync()
Threading.Thread.Sleep(1000)
ToolStripStatusLabel1.Text = "Standing by..."
Me.Refresh()
End Sub**strong text**
I had a 20second delay but still the first BGW does not finish. I know this is something simple but I dont understand, that's all I am after here.
I DID change the code and do not use the same methodology as I was trying at the time I wrote this question... What I do not understand is why a simple operation is never, apparently, finishing... having said that, it DOES finish as I was able to show a msgbox using the runworkercompleted event. So, as I tried and failed to convey, thbis is not about the right or wrong way to code, I know it wa wrong and was just trying to be quick and dirty, regardless of that, I am not doing that now, but I do not understand why the BGW is "still working". There must be some simple thing I am ignorant about.
Thanks

The error is not in the posted code but where you start the Bgw.
But it is all irrelevant because you should not touch the GUI from DoWork:
Private Sub BackgroundWorker1_DoWork(...) Handles BackgroundWorker1.DoWork
'CHECK THE STATE OF THE PROGRESS BAR AND TOGGLE IT
If ToolStripProgressBar1.MarqueeAnimationSpeed = 0 Then ' Boom, cross-threading violation
ToolStripProgressBar1.MarqueeAnimationSpeed = 22
End If
I don't think you need a Bgw, thread or timer here. Just change the speed before/after the slow action.

Related

How to cause a running VB program to start from scratch again

How can I restart a running VB program from within?
I have written a VB program that solves SUDOKU puzzles.
When it completes the solution, it asks if the user wants to run again with another puzzle.
I can not:
-determine the first line of code that the program executes. The main() is empty. All code is on the tab for Form1. I have tried breakpoints, but still can't determine the starting point.
-figure out how to cause the program to jump back to the starting point in the code.
Can you help me?
I've tried placing breakpoints in the code at places that might be close to the starting point, but so far that hasn't worked. When the program starts, Form1 is displayed, and it is waiting to see if a check box gets activated, or if text is entered anywhere in many text boxes.
You should do what Alejandro said in his comment: "reset the data state to the starting point".
But if you must, you can restart a WinForms application with Shell(Application.ExecutablePath)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If MessageBox.Show("Start from scratch?", "Confirm Restart", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = DialogResult.Yes Then restart()
End Sub
Private Sub restart()
Shell(Application.ExecutablePath, AppWinStyle.NormalFocus, False)
End
End Sub

How to properly interact with UI in threading in VB.NET

I'm new to VB.NET threading
As for simple testing I tried the following, which I need to smoothly fill a listbox with values.
But it does not work as I expect, it hangs the interface. Please let me know what I'm doing wrong here.
Thank you.
Imports System.Threading
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim Thr As Threading.Thread
Thr = New Threading.Thread(New Threading.ThreadStart(AddressOf tprocess))
'Thr.SetApartmentState(ApartmentState.STA)
Thr.IsBackground = True
Thr.Start()
End Sub
Private Delegate Sub DoStuffDelegate()
Private Sub tprocess()
Dim i As Integer
For i = 0 To 20000
If Me.InvokeRequired Then
Me.Invoke(New DoStuffDelegate(AddressOf tprocess))
Else
ListBox1.Items.Add(i)
End If
Next
End Sub
End Class
When you write code to create a thread then you always have to worry about the kind of bugs that threading can cause. They are very hard to diagnose, the only decent way to address them is to know they exist and to write the code carefully so you know how to avoid them.
The most common threading bugs are threading races, deadlock and firehose bugs. You have the 1st and the 3rd bug in your code. You are complaining about the 3rd. Very quickly: the threading race bug is using Me.InvokeRequired. You have no guarantee that it is still true when the Me.Invoke() statement executes. This goes wrong when the user closes the window while your thread is still running. When you try to fix this problem you'll get to see what the 2nd bug looks like. But you are not there yet.
The firehose bug is the Me.Invoke() call. Very fast, takes less than a microsecond of work for the worker thread, you do it 20000 times at a very high rate. It is however another thread that must actually do the work of adding the item, your UI thread. That is not fast, it not only has to add the item but it also needs to repaint the control. Many microseconds.
While this goes on, your UI thread is burning 100% core, trying to keep up with the relentless rate of invoke requests. Working as hard as it can to add items to the listbox. Something has to give, while it is doing this it is not taking care of the lower priority jobs it has to do. Painting and responding to user input. In effect, your UI looks completely frozen. You can't see it paint anymore and trying to, say, close the window doesn't work. It isn't actually dead, it is hard at work.
Takes a while, probably a few handful of seconds, give or take. Until the worker thread finishes its for() loop and stops slamming the UI thread with invoke requests. And everything turns back to normal.
A firehose bug like this is pretty fundamental. The only way to fix it is to call Invoke() less often or at a lower rate. Note how putting Thread.Sleep(50) after the Invoke() call instantly fixes it. But of course that slows down your worker thread a lot. You call Invoke() less often by using AddRange() instead of Add(), adding (say) 1000 items at a time. Which is the proper fix but now it becomes fairly pointless to still try to update the listbox from the worker thread. Might as well do it with a single AddRange() call. The quickest way.
Try changing:
Thr = New Threading.Thread(New Threading.ThreadStart(AddressOf tprocess))
to this:
Thr = New Threading.Thread(AddressOf tprocess)
ThreadStart will start that thread immediately
I tried the following way. It's almost easy for me to handle. Backgroudworker manages this situation perfectly well.
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim i As Integer
For i = 1 To 20000
BackgroundWorker1.ReportProgress((i / 20000) * 100, i)
Threading.Thread.Sleep(1)
Next
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
ProgressBar1.Value = e.ProgressPercentage
ListBox1.Items.Add(e.UserState)
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
MsgBox("Complete")
End Sub

Stop a background worker

I am trying to add a STOP button to my program to, stop a background worker. I have had no luck doing it with the following.
This is my button event
Private Sub GOButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles GOButton.Click
If BackgroundWorker1.IsBusy Then
Exit Sub
Else
PullIPs()
End If
End If
End Sub
The PullIPs sub does alot of stuff, and at the end, starts the backgroundworker
BackgroundWorker1.RunWorkerAsync()
Backgroundworker1 kicks off another sub, like so
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
BackgroundWorker1.ReportProgress(50)
PingAll()
End Sub
So, after that maze, I would like a way to stop the backgroundworker mid 'PingAll()'.
Lastly,
Private Sub StopButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles StopButton.Click
BackgroundWorker1.CancelAsync()
End Sub
Note: SupportCancelation IS enabled. I have also looked all over the place, and it looks like I am doing it, how it should work...
It appears that you have looked all over the place except for the one obvious place that you should have looked first, i.e. the Help documentation. The doco for the BackgroundWorker.CancelAsync method has this to say:
CancelAsync submits a request to terminate the pending background
operation and sets the CancellationPending property to true.
When you call CancelAsync, your worker method has an opportunity to
stop its execution and exit. The worker code should periodically check
the CancellationPending property to see if it has been set to true.
Where in your code are you doing as that instructs? Nowhere, so you're obviously not doing it how it should work.
Calling CancelAsync only requests a cancellation. It's still up to you to add code to your DoWork event handler to test whether a cancellation has been requested and stop doing the work if it has. The DoWork event handler can do anything at all so calling CancelAsync is not going to simply abort that on the spot without any consideration for what state the app is in and whether any cleanup may be required.
You know what work is being done so it's up to you write the code such that that work can be cancelled part way through. As it is, all you're doing is a single call to PingAll so there is no way to cancel it. You need to restructure that code, e.g. turn it into a loop that does one ping per iteration and then you can cancel between iterations if required.

How can I get a program to run automatically at specific times a day (VB.NET)

The application I'm developing right now allows the user to update an Excel sheet or Sql database for set metrics twice a day. The program does this by popping up at certain times (e.g. 6:00 AM, 5:00 PM, 3:42 PM, whatever the user sets). By having the program pop up at certain times, the program ("Auto Excel It!!!") allows you as the user to track set data (say, sales calls, sales presentations, meetings, number of hours coding, number of jalepeƱo burritos eaten, etc.).
How can a developer get this program to "pop up"/start/function automatically at specific times through the means of the Windows Scheduler API (or something better)?
Here's how my understanding's evolved lately:
Nothing --> Use Timers As The Program Runs In The Background --> Use Windows Scheduler's API To Run Automatically (Current) --> Possible New Understanding From Your Answer
For example, I'm aware of: DispatcherTimers, Timers, another timer I'm not aware of, Sleep(), Windows Scheduler. But with these in mind, I don't know what to do regarding the following: Automatically starting a program via Windows Scheduler; Preserving computer resources if a timer is used; or even how to get this top pop up automatically.
Update 1:
#nfell2009:Your logic helped me out big time. At first I had to toy around with converting your Timer here to a DispatcherTimer (WPF forms standard, it seems). Then, I switched the the "Handles" for the Sub tCheckTime to "AddHandler tCheckTime.Tick, AddressOf tCheckTime_Tick" --- Why I had to do this is a good question.
Then, once I had the basic EventHandlers set up, your idea for comparing the user's text (As Date) to the System.Date is good--When I screwed something up and couldn't get the code to work, I switched it up and converted System.Date to a String--i.e. I went from String->Date To Date->String... That's when I got the Timer to work. When my System.Time ticked to 3:12 PM, the MsgBox popped up with "Your Message Here."
(A Quick (Evil) Thank You! I've spent four-plus hours getting this to work)
Code:
From Using "Handles" At tCheckTime_Tick (Which seems like it 'should' work)
Private Sub tCheckTime_Tick(sender As Object, e As EventArgs) Handles tCheckTime.Tick
...
End Sub
To AddHandler blah, AddressOf tCheckTime_Tick (Does work)
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Loaded
'MsgBox(Now().ToString("hh:mm")) 'String.Format("{hh:mm}", Now()))
AddHandler tCheckTime.Tick, AddressOf tCheckTime_Tick 'Why is this necessary?
tCheckTime.Interval = New TimeSpan(0, 1, 0)
End Sub
Public Class Form1
Dim iSetTime As Date
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Sub btnSetTime_Click(sender As Object, e As EventArgs) Handles btnSetTime.Click
If (tCheckTime.Enabled = True) Then
tCheckTime.Enabled = False
End If
iSetTime = txtTHour.Text + ":" + txtTMinute.Text + ":" + txtTSecond.Text
tCheckTime.Enabled = True
End Sub
Private Sub tCheckTime_Tick(sender As Object, e As EventArgs) Handles tCheckTime.Tick
If (TimeOfDay = iSetTime) Then
MsgBox("Your Message")
End If
End Sub
End Class
You will need error checking for the textboxs, but its simply:
3 textboxs with indication of which is which, so maybe a label each with H, M, S - or something.
A button which will set time and a timer. Naming:
Textboxs
Hours = txtTHour
Minutes = txtTMinute
Seconds = txtTSecond
Buttons
Start Button = btnSetTime
Timers
Timer = tCheckTime
I can think of two easy ways:
Have your program calculate the time until it should next appear in seconds and then set a timer with an elapsed time such that when the tick event is raised you can do whatever you need to do.
Use MS Task Manager to launch your program when and as needed.

Closing multiple programs with the same name

One part of my program is to close another program or the same program with the same name... I looked up how to do this and got this code:
Dim myprocesses() As Process
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
For Each p As Process In myprocesses
If p.MainWindowTitle.Contains("notepad") Then
p.CloseMainWindow()
End If
Next
End Sub
It should work, however when I run it I'm getting an error message on the Next statement saying:
{"Object reference not set to an instance of an object."}
Does anyone know what's wrong with the above code?
UPDATE: Some of these answers seem to work :) Thanks. However there is a slight problem because sometimes the program doesn't load up until about 30 seconds (notepad was just put there for simplicity) so I need the code to close the PROCESS and not the actual program when it loads.
Hope you can understand that xD ^^^^
Obviously you declared the myprocesses() but null, so to achieve your task you must use GetProcessesByName
For Each process1 As Object In Process.GetProcessesByName("Notepad")
process1.Kill()
Next
But here's the proper way to manage the .exe process.
Try below code in your button click event:
Dim myProc As System.Diagnostics.Process
For Each myProc In System.Diagnostics.Process.GetProcesses
Console.WriteLine(myProc.MainWindowTitle)
If myProc.MainWindowTitle.ToUpper.Contains("NOTEPAD") Then
myProc.CloseMainWindow()
End If
Next