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
Related
I have a vb.net application processing a large amount of data. Due to the memory requirements of the process I am doing this batch-wise, with an overall planned structure as follows:
Do while Start < TotalNumberOfObjects
[cache data used for the upcoming batch]
For i = Start to Stop
[process data using multiple tasks...for example:]
t=taskfactory.startnew(doStuff(i))
TaskList.TryAdd(t.ContinueWith(Sub()
Me.BeginInvoke(DelegateUpdateProgress, {progress})
End Sub))
Next
[Wait for tasks to complete...
Normally I would wait for the tasks using task.waitall(),
but this will cause the UI to wait to update until all tasks are complete]
Start = Stop+1
Stop = Stop+Increment
[clear data from batch that was just completed]
loop
What's the proper way to:
Wait for all the tasks to complete before moving to the next batch?
Update the UI with the overall progress as each task completes?
My target framework is .NET 4.0.
I appreciate any input.
EDIT: Currently I am updating the UI upon completion of each task using task.continuewith() and calling me.beginInvoke to update the form,
TaskList.TryAdd(t.ContinueWith(Sub()
Me.BeginInvoke(DelegateUpdateProgress, {progress})
End Sub))
However, this is incompatible with how I would expect to wait for a list of tasks to complete, task.waitall(tasklist) because calling task.waitall will cause the UI thread to wait to update until all the tasks are complete.
First you need to set up a delegate and then use Dispatcher.Invoke
In the example below, a button is changed from enabled to disable (or the other way around):
Delegate Sub SetRecordButtonEnabledCallback(ByVal Enabled As Boolean)
Friend Sub SetRecordButtonEnabled(ByVal Enabled As Boolean)
Me.btnDGRecord.IsEnabled = Enabled
End Sub
after that all you need to do is call the following code from within your timer to invoke it:
Dim DesiredValue as Boolean = True
Me.Dispatcher.Invoke(New SetRecordButtonEnabledCallback(AddressOf SetRecordButtonEnabled), New
Object() {DesiredValue})
Why don´t you put your routine in a Backgroundworker structure?
So, while you code process data, you´re ready to update any UI component you have.
If necessary, you may also update UI from Backgroundworker, but you need to have some special requirements to do this.
You may consult here:
https://msdn.microsoft.com/en-us//library/ywkkz4s1.aspx
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
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.
I'm trying to figure why my form freezes up when executing some code. I also can't minimize or move the form. Is it because of the WaitForExit being used in the process?
The below code is tied to a button click.
If Checkbox1.checked = True Then
Call Test()
End If
If Checkbox2.checked = True Then
Goto NextStep
Else
Goto StopProcessing
End If
Here is the test sub I'm calling. Calls an exe with an optional argument.
Using psinfo As New Process
psinfo.StartInfo.FileName = "C:\Temp\Test.exe "
psinfo.StartInfo.Arguments = Arg1
psinfo.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
psinfo.Start()
psinfo.WaitForExit()
End Using
The WaitForExit was added (so I thought) to not process the next statement (next statement being the If statement for Checkbox2) until the process was complete. Is this not the case?
The WaitForExit was added (so I thought) to not process the next statement (next statement being the If statement for Checkbox2) until the process was complete.
When you call WaitForExit, it will block until the process (Test.exe) completes.
Since you're running this on the user interface thread, it will cause your form to "freeze" until the process completes fully.
If you need this to not occur, you would need to wait on a background thread. You could, potentially, move this code into a BackgroundWorker and use it to synchronize with your main window - but you will need to handle "waiting" for the process to finish in a different manner (ie: disable your UI up front, run the process, re-enable when complete).
Note that, with the Process class, another alternative would be to add EnableRaisingEvents on the process, then adding a handler to Process.Exited. This will let you not WaitForExit(), but instead get notified via an event when the process completes.
Team,
I have build a VB.Net windows application which does uploads data into database and basically updates two controls:
1. A textbox which is constantly updated with one line per database record upload.
2. A label which keeps track of the count of database record uploaded.
I have used BackgroundWorker thread concept, where the thread's bgwWorker_DoWork() method contains the business logic for upload and bgwWorker_ProgressChanged() updates the 2 UI controls based on uploads.
But the issue I am facing is that I do not get complete updates on both the UI controls. Sometimes the thread bypasses update of textbox and sometimes of label. I could resolve this issue by adding System.Threading.Thread.Sleep(25) before each UI control update code. Is this correct way of solving the issue? OR is there something I am missing?
Kindly suggest.
Below is the code in both these methods:
Private Sub bgwWorker_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bgwWorker.DoWork
.................
.................
'Updates database record related update in textbox
System.Threading.Thread.Sleep(25)
updater.eventName = "UpdateStatusBox"
updater.errorMessageToLog = String.Empty
updater.errorMessageToLog += GetErrorMessage(dataTable(rowNumber)("Name").ToString(), ExceptionData)
bgwWorker.ReportProgress(1, updater)
.................
.................
'Updates Status Count in LABEL
System.Threading.Thread.Sleep(25)
updater.eventName = "UpdateStatusBar"
updater.successCount = successCount.ToString()
updater.failureCount = failureCount.ToString()
bgwWorker.ReportProgress(2, updater)
End Sub
Private Sub bgwWorker_ProgressChanged(ByVal sender As System.Object, ByVal e As ProgressChangedEventArgs) Handles bgwWorker.ProgressChanged
Dim updater As UIUpdater = TryCast(e.UserState, UIUpdater)
..........................................
If updater.eventName = "UpdateStatusBar" Then
UpdateStatusBar(updater.successCount, updater.failureCount)
ElseIf updater.eventName = "UpdateStatusBox" Then
txtUpdates.Text = txtUpdates.Text & updater.errorMessageToLog
End If
.....................................
End Sub
I'm almost positive that your problem is your instance of the UIUpdater object called updater. This object appears to be declared globally and is thus shared between calls.
Omitting a little bit of code this is what you have:
updater.eventName = "UpdateStatusBox"
bgwWorker.ReportProgress(1, updater)
updater.eventName = "UpdateStatusBar"
bgwWorker.ReportProgress(2, updater)
Although you call ReportProgress() linearly, it doesn't fire your ProgressChanged event immediately nor does it block until that method completed. To do so would defeat the purpose of threading if you think about it.
To put it another way, you have a global object that you are setting a property on. You then say "when someone gets a chance, do something with this". You then change a property on that global object and sometimes this happens before "someone has done something" happens.
The solution is either to create two global variables, one for each possible event or to just create an instance variable when needed. I'm not sure that its thread safe to use a global variable the way you are so I would recommend just creating an instance variable. In fact, the state object you pass to ReportProgress could just be a string.
I would NOT use a sleep in your DoWork event.
Have you tried refreshing the control after you update it? Each control has a Refresh method which forces a redraw. This may result in flickering though.
Another option is to include the information needed for both controls (textbox and label) in a single call to ReportProgress rather than trying to make two calls.