im running a backgroundworker which is calling a function to download huge files and folders. im not able to stop this through cancelasync. The cancelationpending event is received, but my function still calculates.
How can I stop the thread?
Private Sub copydownloads_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles copydownloads.DoWork
DirectoryCopy()
End Sub
public sub DirectoryCopy()
'here is the work done
end sub
CancelAsync wont magically stop BackgroundWorker. You have to manually poll CancelPending property in your download code.
The worker code should periodically check the CancellationPending property to see if it has been set to true.
When you see that CancellationPending is set to true, you have to stop your downloading (ie. download small chunks in a loop and check flag constantly).
https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancelasync%28v=vs.110%29.aspx
See this question for an example code how to download file in chunks. Add CancelPending checking inside that code:
Download File With Start / Pause / Stop
Related
I have a custom class in VBA that pulls historical data from Bloomberg. The class, and the Bloomberg objects it uses, are asynchronous and based on the RTD platform.
The issue I'm having is that I run Subs that call this custom class, but the event handling code in the custom class only runs once my Sub is finished.
Dim bbHist As New HistDataControl
Sub PullDataAndDoStuff()
bbHist.MakeHistRequest StockTicker, "MOV_AVG_50D", startDate, Date
Call DoStuffWithTheData
End Sub
Private Sub DoStuffWithTheData()
..... 'None of this works, because MakeHistRequest / bbHist class hasn't run
End Sub
Is there a way to force Excel to wait until the bbHist has run?
If it has a property to check to see if it's done, then you can just put a DoEvents in a Do Until loop, checking that property for its completion.
The problem is that the bbg handler waits.
So the solution is to make your sub wait for the bbg query to end, and then call your processing data sub.
There are plenty of solutions for that on stackoverflow so I'll let you look for that.
Is there anyway to have an IF statement constantly checking? I'm working with a List and I need to check if the list count exceeds a variable. I've since noticed the easiest way to make the program run smoothly is to have an IF statement checking constantly while the program is running.
The best approach for this would be to change from 'List' to 'BindingList'. This is an event enabled list which will fire events when the list changes:
Private WithEvents mList As New System.ComponentModel.BindingList(Of String)
Public Sub Main()
mList.Add("An Item")
End Sub
Private Sub mList_AddingNew(sender As Object, e As System.ComponentModel.AddingNewEventArgs) Handles mList.AddingNew
If mList.Count > 100 Then
MessageBox.Show("Threshold exceeded")
End If
End Sub
Alernatively you could start a thread / timer that polls this, however you'll have to watch out for synchronization issues.
I have started a process:
Dim getUdpate as Process
getUpdate = New Process
getUpdate.StartInfo.FileName = "C:\UTIL\GETBTCH.BAT"
getUpdate.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
getUpdate.StartInfo.UseShellExecute = False
getUpdate.StartInfo.WorkingDirectory = "C:\UTIL\"
getUpdate.Start()
getUpdate.Close()
Then, I want to Run another process but I want to check first if the getUpdate process is already finished.
How do I check if the process is already finished?
I already tried to look at the processes ID, but it only display cmd.exe and there are a lot of cmd.exe as the processes ID so I can't just go and stop all of those.
You can check the HasExited property of the process. It will return true if the process has ended, and false if it is still running.
You will need to check this before you call Close() on your getUpdate Process object. So getProcess will have to remain open until the procsses has exited.
Try:
getUpdate.WaitForExit(); instead of
getUpdate.Close()
If you are making a WinForms application or similarly interactive UI I suggest hooking a function into the object's Exited event instead of polling HasExited.
(You may already know this but) if you use WaitForExit or poll HasExited your UI is hanging exactly because your code is actually waiting for the process to end.
Your UI only has one thread and cannot "multitask". That's why these "processing" type of actions should be done in a different thread (or, as is the case here, a different process) and report back to the UI when they finish.
Example:
' Handle Exited event and display process information.
Private Sub myProcess_Exited(ByVal sender As Object, ByVal e As System.EventArgs)
'Do something in your UI
End Sub
and in your starting code:
getUpdate.EnableRaisingEvents = True
AddHandler getUpdate.Exited, AddressOf myProcess_Exited
I am using DownloadFileAsync to download a larger file (1.3 GB), but i'd like to add a simple percentage indicator (ex. 64%). I'm new to Visual Basic I have no idea how to do this.
Any help would be appreciated.
The WebClient class has a DownloadProgressChanged event that you can listen to if you want to update a progresss display. For instance, if you’ve got a console application, it’s as simple as:
Dim client As New WebClient()
AddHandler client.DownloadProgressChanged, AddressOf ProgressUpdate
client.DownloadFileAsync(yourURI, yourFile)
Sub ProgressUpdate(sender As Object, e As DownloadProgressChangedEventArgs)
' Reset cursor position …
Console.CursorTop -= 1
Console.CursorLeft = 0
Console.WriteLine("{0}% completed", e.ProgressPercentage)
End Sub
If, on the other hand, you are on a Form in a WinForms project and you’ve got a label ProgressLabel that you want to update, the following code will do that:
Sub ProgressUpdate(sender As Object, e As DownloadProgressChangedEventArgs)
Dim s = String.Format("{0}% completed", e.ProgressPercentage)
Me.Invoke(New Action(Sub()
ProgressLabel.Text = s
End Sub))
End Sub
The ProgressUpdate method is a bit complicated due to multithreading:
The WebClient is running the asynchronous file download in a background thread. However, form controls can only be updated from the foreground thread that the form is running in. For that reason, we cannot update the label directly inside the ProgressUpdate event (because that, too, is being invoked, and running, in the background thread1).
So what we do instead is use the Form.Invoke method which guarantees that whatever we want to execute is execute in the form’s own thread. We pass an Action delegate to the Invoke method which contains the code that we want to execute. And that code is just updating the label.
1 At least I couldn’t find anything in the documentation saying otherwise – the event might actually execute in the foreground thread but in that case the above code still works.
Ok, the following codes shows how I am enterting a value into a textbox, adding that value to the listbox, updating a picturebox next to it and blanking out the textbox so the user can add additional values to the listbox.
ListBox1.Items.Add(TextBoxTicketID.Text)
If CStr(ListBox1.Items(0)) = TextBoxTicketID.Text Then
PictureBoxStatus1.Image = My.Resources.Orange_Information
End If
TextBoxTicketID.Text = ""
I have another process not shown here that will create a PDF based on the value that was entered into the listbox.
I'm having trouble with a loop to check a specific directory if the PDF exists or not. When the PDF exists, I'll change the picturebox to another image.
Here is the loop that I was using, but the issue I ran into was that the user couldn't enter a second value unless the first value was present.
Loop Until My.Computer.FileSystem.FileExists("c:\Temp\" + ListBox1.Items(0) + ".pdf")
PictureBoxStatus1.Image = My.Resources.Green_Checkmark
So in theory, I need to be able to enter X amount of values into the listbox and keep checking to see if the file exists and if it does, change those images that needed.
EDIT
Here's what I ended up doing...seems to be working fine though...
ListBox1.Items.Add(TextBoxTicketID.Text)
If CStr(ListBox1.Items(0)) = TextBoxTicketID.Text Then
PictureBoxStatus1.Image = My.Resources.Orange_Information
End If
TextBoxTicketID.Text = ""
Call CheckFiles()
Added a public sub
Public Sub CheckSpooling()
Dim Watcher As New FileSystemWatcher()
Watcher.Path = "C:\Temp\"
Watcher.Filter = ListBox1.Items(0) + ".pdf"
AddHandler Watcher.Created, AddressOf OnChanged
Watcher.EnableRaisingEvents = True
End Sub
Then the sub to run whatever is needed if the file was added. I used a msgbox for testing.
Private Shared Sub OnChanged(source As Object, e As FileSystemEventArgs)
' Specify what is done when a file is created.
MsgBox("File has been created!")
End Sub
Check out the FileSystemWatcher
The reason the user can't enter anything while you are looping is because the WinForm framework is essentially single threaded. Everything in the UI occurs on the same thread, including the event handler. So, if you are sitting in a loop for a long time in a button click event handler, then the UI will be locked up and unresponsive until the code exits the loop. The way to get around this is to start a new thread to perform whatever work needs to be done. That worker thread can take as long as it needs to complete and it won't interfere with the UI thread so the UI remains responsive. This is made easier by the BackgroundWorker component which you can drop onto your forms in the form designer.
However, the FileSystemWatcher, as Dan-o has recommended is probably a better solution than creating your own worker thread that keeps checking if the file exists. Not only does it avoid re-inventing the wheel, but it also will be more efficient. Instead of constantly asking the file system if a file exists, it just listens to messages from the file system to find out when changes occur.