Async Download file updating the gui - vb.net

I am downloading a file using webclient handling DownloadProgressChanged and DownloadFileCompleted.
My code is working whenever I am not updating the gui with percentage,b but In case I am going to update just a single label with it, then it kind of freezing in random situations. Doesn't matter if the file is little or big.
Public WithEvents mclient As New WebClient
Private Sub Download()
Dim filepath As String = (filepath..)
mclient.Encoding = Encoding.UTF8
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12
mclient.Headers.Add(HttpRequestHeader.UserAgent, "")
mclient.DownloadFileAsync(New Uri(link), filepath)
End Sub
Private Sub mClient_DownloadProgressChanged(sender As Object, e As DownloadProgressChangedEventArgs) Handles mclient.DownloadProgressChanged
Try
'file website missing Content-Length, so getting the real size parsing html (real size=label7)
label1.text= ((e.BytesReceived) / 1048576).ToString("0.00") & "/" & Label7.Text
Catch ex As Exception
End Try
End Sub
Private Sub mClient_DownloadFileCompleted(sender As Object, e As AsyncCompletedEventArgs) Handles mclient.DownloadFileCompleted
Try
label2.text="Download ended"
Catch ex As Exception
End Try
End Sub
How can I keep label1.text updated without using it inside of DownloadProgressChanged?
If I use a variable on it, where can I keep it updated? I don't want to use a timer though..

Related

Cleanly stopping a thread in VB.net to avoid double error handling

I’ve got this issue with stopping a thread cleanly. I’ve tried to simplify it into a more basic version of the code below and I’m wondering if my approach is completely wrong here.
I have Form1 with a bunch of UI elements which need updating as BackgroundCode runs (I run it here so it’s a separate thread and it doesn’t hold up the UI) I then update the UI by invoking a sub
(Me.Invoke(Sub()
something.property=something
End Sub))
I’m also trying to handle some errors handed to the application by an external file. I’ve used a timer to check for the file and if it exists I grab the contents and pass it to my ErrorHandler. This Writes the Error out to a log file, displays it on screen and then aborts the background worker so that the program doesn’t continue to run. The trouble I’m getting is that by executing BackgroundThread.Abort() that action itself is triggering the ErrorHandler. Is there a way to ask the BackgroundThread to stop cleanly? I want BackgroundThread to trigger the ErrorHandler if something else goes wrong in that code.
I’m wondering about using a global boolean like “ErrorIsRunning” to restrict the ErrorHandler sub so that it can only ever run once, but this is starting to feel more and more hacky and I’m wondering if I’ve gone completely off track here and if there might be a better way to approach the entire thing.
Public Class Form1
Dim BackgroundThread As New Thread(AddressOf BackgroundCode)
Public Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
‘Hide Error Page
ErrorPage.Visible = False
ErrorLabel.Visible = False
‘Start Background Code
BackgroundThread.Start()
End Sub
Private Sub BackgroundCode()
Try
‘<Background code which runs over a number of minutes>
Catch.ex as Exception
ErrorHandler(“Error with BackgroundCode: “ + ex.Message)
End Try
End Sub
Private Sub Timer_Tick(sender As Object, e As EventArgs) Handles Timer.Tick
Dim ErrorFile As String = “C:\MyErrorFile.Err”
Dim ErrorContents As String
If File.Exists(ErrorFile) Then
Timer.Enabled = False
ErrorContents = File.ReadAllText(ErrorFile).Trim()
ErrorHandler(ErrorContents)
End If
End Sub
Public Sub ErrorHandler(ErrorText As String)
WriteLog(“ERROR” + ErrorText)
Me.Invoke(Sub()
Me.ErrorPage.Visible = True
Me.ErrorLabel.Text = ErrorText
End Sub)
BackgroundThread.Abort()
End Sub
End Class
Never abort threads.
This uses a Task and a ManualResetEvent. Without seeing the code inside of the background task it is hard to know how many stop checks might be needed.
Public Class Form1
Private BackgroundTask As Task
Private BackgroundTaskRunning As New Threading.ManualResetEvent(True)
Public Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Hide Error Page
ErrorPage.Visible = False
ErrorLabel.Visible = False
'Start Background Code
BackgroundTask = Task.Run(Sub() BackgroundCode())
End Sub
Private Sub BackgroundCode()
Try
'<Background code which runs over a number of minutes>
'put stop checks periodically
' e.g.
If Not BackgroundTaskRunning.WaitOne(0) Then Exit Sub 'stop check
Catch ex As Exception
ErrorHandler("Error with BackgroundCode: " + ex.Message)
End Try
End Sub
Private Sub Timer_Tick(sender As Object, e As EventArgs) Handles Timer.Tick
Dim ErrorFile As String = "C:\MyErrorFile.Err"
Dim ErrorContents As String
If File.Exists(ErrorFile) Then
Timer.Enabled = False
ErrorContents = File.ReadAllText(ErrorFile).Trim()
ErrorHandler(ErrorContents)
End If
End Sub
Public Sub ErrorHandler(ErrorText As String)
WriteLog("ERROR" + ErrorText)
Me.Invoke(Sub()
Me.ErrorPage.Visible = True
Me.ErrorLabel.Text = ErrorText
End Sub)
BackgroundTaskRunning.Reset() 'stop task <<<<<<<<<<<<<<<<<<<<<<<<<<<
End Sub
End Class

DownloadCompleteEvent fires twice

I have a windows form which lets the user select a file name and download it, Once the download is complete, a download finished event is triggered which unzips the downloaded file and processes it further.
The downloadComplete event seems to be triggered twice which is causing the unzipping action to happen twice. Is there a reason why the event would be fired twice?
below is the function which triggers the download and Handles the download complete event.
Private Async Sub Btn_download_Click(sender As Object, e As EventArgs) Handles Btn_download.Click
Dim fileNameRows As DataGridViewSelectedRowCollection = datagridview_cloudContent.SelectedRows
Dim fileName As String
Dim fileType As String = AWSGlobals.CONTENT
For Each fileNameRow As DataGridViewRow In fileNameRows
fileName = fileNameRow.Cells(0).Value.ToString() & ".zip"
Try
Await DownloadAsyncFile(fileName, fileType)
Catch ex As Exception
CSMessageBox.ShowError("Content Import failed : ", ex)
End Try
Next
End Sub
Private Function UnzipAndImport(filename As String) Handles s3obj.DownloadDone
If Not System.IO.Directory.Exists(AWSGlobals.ContentPath & Path.GetFileNameWithoutExtension(filename)) Then
Dim zipFilePath As String = DiskPath & filename
Dim zipFileArchive As ZipArchive = ZipFile.OpenRead(zipFilePath)
Dim FullPathOfContent As String = DiskPath
ExtractToDirectoryOverWrite(zipFileArchive, FullPathOfContent, True)
ImportExport.BatchImportContent(New ArrayList(New String() {ExtractedPath}), objmanager)
End If
End Function
In the class that handles the downloads, the download method is implemented as below :
Public Function DownloadAsyncFile(FileName As String, FileType As String)
Try
Dim Address As String
AddHandler fileReader.DownloadFileCompleted, Sub(sender, e) download_complete(FileName)
AddHandler fileReader.DownloadProgressChanged, AddressOf Download_ProgressChanged
fileReader.DownloadFileAsync(New Uri(Address), AWSGlobals.ContentPath + FileName)
Catch ex As Exception
MsgBox( ex.Message)
End Try
End If
End Function
Private Sub download_complete(filename As String)
RaiseEvent DownloadDone(filename)
End Sub
Can someone tell me if this implementation causes the download complete event to fire twice when just one file is being downloaded?

Wait until download is complete before starting other tasks

I am trying to download a file, and then run some adb commands. However when downloading, i could not get it to update the progress bar using
downloadFile(url,filename)` command.
A bit of searching it said that the command was blocking the UI thread, so i decided to use Task.Run() (a solution to my previous post to run ADB Commands when it blocked the UIThread).
This made no difference. Another solution that i found is to use
downloadFileAsync(url, filename)
The progress bar is updating!
But, the ADB commands are running before the file is downloaded! They are declared after, but they are still being run before the file is downloaded, which I don't want.
Here is the code:
Private Sub btnFlashRecovery_Click(sender As Object, e As EventArgs) Handles btnFlashRecovery.Click
'Insert ommited Code here (removed to simplify question)
'id is variable obtained from a previous code that was ommited here
Dim fileName As String = "downloads/twrp-" & id & ".img"
DownloadFile(url, fileName)
'run the right commands
LabelToOutput = txtBoxRecovery
Dim commands(3, 3) As String
commands = {{"adb", "reboot bootloader", "Rebooting to bootloader"},
{"fastboot", "flash recovery" & "downloads/twrp-3.1.1-0.img", "Flashing recovery: (make sure device is plugged, otherwise it will not output anything)"},
{"fastboot", "reboot", "Rebooting device"}
}
'Task to run after
Task.Run(Sub() runComands(commands))
End Sub
Private Sub UpdateProgressBar(ByVal a As Integer)
If Me.InvokeRequired Then
Dim args() As String = {a}
Me.Invoke(New Action(Of String)(AddressOf UpdateProgressBar), args)
Return
End If
ProgressBar1.Value = CInt(a)
End Sub
Public Sub DownloadFile(urlAddress As String, location As String)
Using webClient = New WebClient()
AddHandler webClient.DownloadFileCompleted, AddressOf Completed
AddHandler webClient.DownloadProgressChanged, AddressOf ProgressChanged
Try
' Start downloading the file
webClient.DownloadFileAsync(New Uri(urlAddress), location)
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Using
End Sub
' The event that will fire whenever the progress of the WebClient is changed
Private Sub ProgressChanged(sender As Object, e As DownloadProgressChangedEventArgs)
'Console.WriteLine(e.ProgressPercentage)
' Update the progressbar percentage only when the value is not the same.
UpdateProgressBar(e.ProgressPercentage)
End Sub
' The event that will trigger when the WebClient is completed
Private Sub Completed(sender As Object, e As AsyncCompletedEventArgs)
If e.Cancelled = True Then
MessageBox.Show("Download has been canceled.")
Else
MessageBox.Show("Download completed!")
End If
End Sub
Do it like this:
'Need Async keyword with the method
Private Async Sub btnFlashRecovery_Click(sender As Object, e As EventArgs) Handles btnFlashRecovery.Click
'Insert ommited Code here (removed to simplify question)
'id is variable obtained from a previous code that was ommited here
Dim fileName As String = "downloads/twrp-" & id & ".img"
'You need to AWAIT the result of the task
Await Task.Run(Sub() DownloadFile(url, fileName))
'run the right commands
LabelToOutput = txtBoxRecovery
Dim commands(3, 3) As String
commands = { {"adb", "reboot bootloader", "Rebooting to bootloader"},
{"fastboot", "flash recovery" & "downloads/twrp-3.1.1-0.img", "Flashing recovery: (make sure device is plugged, otherwise it will not output anything)"},
{"fastboot", "reboot", "Rebooting device"}
}
'Task to run after
'Await here, too, to allow the UI to remain responsive
Await Task.Run(Sub() runComands(commands))
End Sub

File not being written to in vb.net

I did some google searching and everyone gives me the same answer which isn't working. I hope its something simple I am missing. I am trying to test write a line to a txt file. The file was created just fine when I used similar code, and no errors are thrown, the txt just doesn't write/save to the file. I am using stream writer in VB.
Here is my code:
Imports System.IO
Public Class Form1
Private Sub btnGen2DArray_Click(sender As Object, e As EventArgs)
Handles btnGen2DArray.Click
Try
'this is the file created and where it is saved:
Dim fileLoc As String = "c:\Users\clint\save.txt"
If File.Exists(fileLoc) Then
Using sw As New StreamWriter(fileLoc)
sw.Write("Test line write")
sw.Close()
End Using
End If
MsgBox("C++ 2D array text file created in: " + fileLoc, MsgBoxStyle.OkOnly, "Successful")
Catch ex As Exception
MsgBox("error: " + e.ToString(), MsgBoxStyle.OkOnly, "Error")
End Try
End Sub
Private Sub btnClose_Click(sender As Object, e As EventArgs) Handles btnClose.Click
Close()
End Sub
End Class
I am using vb 2012 if this helps. Normal windows form application.
You need to close your StreamWriter when you are done. sw.Close
You must call Close to ensure that all data is correctly written out
to the underlying stream.
Better yet, use Using. The following would go inside your if:
Using sw As New StreamWriter(fileLoc)
sw.Write("Test line write")
For rowcount As Double = 1 To rows
For colcount As Double = 1 To cols
'when the file write test works I will finish the rest of the code here
Next
Next
End Using
This will automatically dispose of the StreamWriter for you.

How to catch exception when attempting to delete a file in use?

I't making a program to delete the files in my temp folder. I've gotten as far as the code to delete the files, But I can't seem to figure out how to skip the files in use or catch the exception so that my program doesn't crash when it attempts to delete a file in use.
Here is the code I have so far:
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
If CheckBox1.Checked = True Then
Dim s As String
For Each s In System.IO.Directory.GetFiles("C:\Users\" + System.Environment.UserName + "\AppData\Local\Temp")
System.IO.File.Delete(s)
Next s
End If
end sub
Use a Try/Catch block to catch errors (exceptions)
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
If CheckBox1.Checked = True Then
Dim s As String
For Each s In System.IO.Directory.GetFiles("C:\Users\" + System.Environment.UserName + "\AppData\Local\Temp")
Try
System.IO.File.Delete(s)
Catch ex As Exception
Console.WriteLine("File could not be deleted : {0}", ex.Message)
End Try
Next s
End If
end sub
That will allow your program to ignore the error and continue processing the next items.
When an exception is thrown, once it is handled by the Catch, execution will jump to the End Try. Therefore, where you place the beginning and ending of your Try/Catch block is important. For instance:
Try
Dim s As String
For Each s In System.IO.Directory.GetFiles("C:\Users\" + System.Environment.UserName + "\AppData\Local\Temp")
System.IO.File.Delete(s)
Next s
Catch ex As IOException
End Try
In the above example, if any call to Delete fails, it will jump to the End Try and skip the rest of the iterations of the For loop (thereby skipping the rest of the files). However, consider this:
Dim s As String
For Each s In System.IO.Directory.GetFiles("C:\Users\" + System.Environment.UserName + "\AppData\Local\Temp")
Try
System.IO.File.Delete(s)
Catch ex As IOException
End Try
Next s
In this second example, it will jump to the End Try and then continue to the next iteration of the For loop (thereby continuing on the the next file in the list).
Also, as noted in the comments, above, you definitely ought to be using Path.GetTempPath to get the path to the temporary folder. You should not construct the path yourself, since it could change. For instance, Windows does not require that you user folder has to be under C:\Users. You can actually change that.