"For Each" loop : Application Freeze in Vb.net - vb.net

I am using the following code to get the size of files inside a directory
and put it in Label1:
For Each foundFile As String In My.Computer.FileSystem.GetFiles( _
"\windows",Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly,_
"*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
Label1.Text = Label1.Text + filesizelabel.Length
Next
The problem is that i have more than 50 for each loops (a system cleaning app).
When I run the loops my app freezes until the loops finish, even if I run one loop.
Is there a solution to make it show the name of the current file? I tried this as well, but it also froze my application:
label2.text = foundfile
The application does not respond to any click, until it finishes the loops. It shows the size in Label1 and the last scanned file in Label2. This also freezes the application:
system.threading.thread.sleep(100)
Is there any alternative to foreach or a solution to fix this issue?

Here's a quick example using Async/Await with a Button Click() Handler:
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
Await Task.Run(Sub()
' this runs in a different thread without blocking the GUI:
For Each foundFile As String In My.Computer.FileSystem.GetFiles(
"\windows", Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly, "*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
' when you need to update the GUI:
Me.Invoke(Sub()
' ... do it in here ...
Label1.Text = Label1.Text + filesizelabel.Length
End Sub)
Next
End Sub)
Button1.Enabled = True
End Sub
For VB.Net 2010, try this instead:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
Dim T As New System.Threading.Thread(AddressOf Worker)
T.Start()
End Sub
Private Sub Worker()
' this runs in a different thread without blocking the GUI:
For Each foundFile As String In My.Computer.FileSystem.GetFiles(
"\windows", Microsoft.VisualBasic.FileIO.SearchOption.SearchTopLevelOnly, "*.*")
Dim filesizelabel As System.IO.FileInfo = My.Computer.FileSystem.GetFileInfo(foundFile)
' when you need to update the GUI:
Me.Invoke(Sub()
' ... do it in here ...
Label1.Text = Label1.Text + filesizelabel.Length
End Sub)
Next
Me.Invoke(Sub()
Button1.Enabled = True
End Sub)
End Sub

This is a prime candidate for a background worker.
Have a read about how they work, but at a high level the task is run in another thread with some events that you access in your main UI thread.
Private bw As BackgroundWorker = New BackgroundWorker
Private Sub buttonStart_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
If Not bw.IsBusy = True Then
' this will start the work
bw.RunWorkerAsync()
End If
End Sub
Private Sub buttonCancel_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
If bw.WorkerSupportsCancellation = True Then
' this will allow the user to cancel the work part way through
bw.CancelAsync()
End If
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
' your slow code goes here
End Sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
' you can update the UI here to show progress
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
' your 'I've finished notification' code goes here
End Sub

Derek has put a bad code, it's not working and Idle mind code does not work on .NET 2.0
Dereks approach is working if code is complete, as below:
Private bw As BackgroundWorker = New BackgroundWorker
Private Sub app_Load(sender As Object, e As EventArgs) Handles MyBase.Load
AddHandler bw.DoWork, AddressOf bw_DoWork
AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged
AddHandler bw.RunWorkerCompleted, AddressOf bw_RunWorkerCompleted
..
End sub
Private Sub Btn_Click(sender As Object, e As EventArgs) Handles Btn.Click
If Not bw.IsBusy = True Then
' this will start the work
bw.RunWorkerAsync()
End If
End Sub
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
'your work to not freeze form
end sub
Private Sub bw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
' you can update the UI here to show progress
End Sub
Private Sub bw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
' your 'I've finished notification' code goes here
End Sub

Related

Unload Form1 when embedded cmd process ends

I have found some code which runs a cmd.exe shell interactively in a TextBox; later on I will replace cmd.exe with a different character based application.
Here's the code:
Public Class Form1
Dim P As New Process
Dim SW As System.IO.StreamWriter
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Text = "My title"
AddHandler P.OutputDataReceived, AddressOf DisplayOutput
P.StartInfo.CreateNoWindow() = True
P.StartInfo.UseShellExecute = False
P.StartInfo.RedirectStandardInput = True
P.StartInfo.RedirectStandardOutput = True
P.StartInfo.FileName = "cmd"
P.Start()
P.SynchronizingObject = TextBox1
P.BeginOutputReadLine()
SW = P.StandardInput
SW.WriteLine()
End Sub
Private Sub DisplayOutput(ByVal sendingProcess As Object, ByVal output As DataReceivedEventArgs)
TextBox1.AppendText(output.Data() & vbCrLf)
End Sub
Private Sub Textbox1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox1.KeyPress
Static Line As String
If e.KeyChar = Chr(Keys.Return) Then
SW.WriteLine(Line & vbCrLf)
Line = ""
Else
Line = Line & e.KeyChar
End If
End Sub
End Class
When you enter the exit command, the cmd.exe process terminates.
I like my application to unload Form1 when this occurs, but I don't know how to implement this.
As suggested by Jimi, I added the following line to he Form1_Load sub:
P.EnableRaisingEvents = True
and added:
Private Sub myProcess_Exited(ByVal sender As Object, ByVal e As System.EventArgs) Handles P.Exited
Me.Close()
End Sub
This is working; thank you very much Jimi!
Add this above End Sub in your Form1_Load Sub:
p.WaitForExit()
Form1.Close()
Since it looks like you're calling it from Form1 itself, you could also use Me.Close.
If Form1 is the only form and you want the whole application to close, you can use Application.Exit() instead.
Some references:
http://www.vb-helper.com/howto_net_start_notepad_wait.html
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.form.close?view=windowsdesktop-6.0

Late Binding Issue with BackgroundWorker in VB.Net

I am running a BackgroundWorker, and want to report its progress. In the example below I create a test list which the BackgroundWorker then iterates through. The problem lies in the line 'sender.ReportProgress(i)'. If I have Option Strict on, it does not like my use of 'i' due to Late Binding issues. Is there any alternative way to code this and avoid that issue?
Public Class Form1
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
' Configuring for Background Workers
Control.CheckForIllegalCrossThreadCalls = False
Dim MyList As New List(Of String)
For a As Integer = 0 To 100
MyList.Add(CStr(a))
Next
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim bgw As New System.ComponentModel.BackgroundWorker
bgw.WorkerReportsProgress = True
bgw.WorkerSupportsCancellation = True
AddHandler bgw.DoWork, AddressOf bgw_DoWork
' I create a BackgroundWorker here rather than add one in the toolbox so that I can specify the Handler and use different Handler routines for different part of a large program.
Button1.Enabled = False
Dim progress As New Progress(bgw)
progress.ShowDialog()
Button1.Enabled = True
End Sub
Private Sub bgw_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs)
For i = 0 To MyList.Count -1
Label1.Text = MyList(i)
sender.ReportProgress(i)
System.Threading.Thread.Sleep(200)
Label1.Refresh()
Next
End Sub
End Class
Public Class Progress
Private WithEvents _BGW As System.ComponentModel.BackgroundWorker
Public Sub New(ByVal BGW As System.ComponentModel.BackgroundWorker)
_BGW = BGW
InitializeComponent()
End Sub
Private Sub frmProgress_Shown(sender As Object, e As System.EventArgs) Handles Me.Shown
If Not IsNothing(_BGW) Then
_BGW.RunWorkerAsync()
End If
End Sub
Private Sub _BGW_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles _BGW.ProgressChanged
ProgressBar1.Value = e.ProgressPercentage
Label1.Text = e.ProgressPercentage
End Sub
Private Sub _BGW_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles _BGW.RunWorkerCompleted
Me.Close()
End Sub
End Class
CType(sender, BackgroundWorker).ReportProgress(i)
Also, if you want to do multiple actions with it, then create a local reference variable like this:
Private Sub bgw_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs)
Dim bgw As System.ComponentModel.BackgroundWorker = DirectCast(sender, System.ComponentModel.BackgroundWorker)
' ... now you can use "bgw" multiple times below instead of casting each time ...
For i = 0 To MyList.Count -1
Label1.Text = MyList(i)
bgw.ReportProgress(i)
bgw.SomethingElse()
bgw.MoreStuff()
System.Threading.Thread.Sleep(200)
Label1.Refresh()
Next
End Sub
Obviously this isn't necessary in your case, just an FYI...

Delete file after "x" second after creation

I want to delete file after 15 second of create that file.
I use this code but no success.
Private Sub Form1_Click(sender As Object, e As EventArgs) Handles Me.Click
Dim test = Application.StartupPath & "\" + tte4
Timer1.Enabled = True
If Timer1.Interval = 0 Then
My.Computer.FileSystem.DeleteFile(test)
End If
End Sub
You must handle the timed event in a handler for the Timer's Tick Event (or Elapsed if using System.Timers.Timer):
Private m_strTest As String = String.Empty
Private Sub Form1_Click(sender As Object, e As EventArgs) Handles Me.Click
m_strTest = Application.StartupPath & "\" + tte4
Timer1.Enabled = True
End Sub
If using System.Forms.Timer (most likely):
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
System.IO.File.Delete(m_strTest)
Timer1.Enabled = False
End Sub
If using System.Timers.Timer:
Private Sub Timer1_Elapsed(ByVal source As Object, ByVal e As ElapsedEventArgs) Handles Timer1_Elapsed
System.IO.File.Delete(m_strTest)
Timer1.Stop()
End Sub
If you added your Timer from the Toolbox, the above code will work (due to WithEvents). If you added it programatically, you'll also have to wire up the Event Handler and remove the Handles from your Timer1_Tick Method:
Private Sub Form1_Click(sender As Object, e As EventArgs) Handles Me.Click
m_strTest = Application.StartupPath & "\" + tte4
AddHandler Timer1.Tick, AddressOf Timer1_Tick
Timer1.Enabled = True
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs)
System.IO.File.Delete(m_strTest)
Timer1.Enabled = False
End Sub
You need define a method that the timer fires when the interval elapses.
' Timer set to fire every 15 seconds
Dim Timer1 As New Timer(15000)
' OnTimedEvent is the method that will fire after 15 seconds
AddHandler Timer1.Elapsed, AddressOf OnTimedEvent
' Start the Timer
Timer1.Start()
The OnTimedEvent is as followed:
Private Sub OnTimedEvent(source As Object, e As ElapsedEventArgs)
' Stop the timer from firing again
Timer1.Stop()
' Delete your file
End Sub
Reference
https://msdn.microsoft.com/en-us/library/k0wdddfy(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1
You have the right idea in using a Timer to wait for the deletion, but your way of trying to use it is incorrect. A (System.Windows.Forms) Timer raises an event when it ticks, and you need to tie that event to the code you want to run.
Also, to combine parts of a path it is better to use IO.Path.Combine, and when doing file operations it is a good idea to "wrap" the relevant code in a Try..Catch construct and handle the exception if it happens.
So:
Option Infer On
Option Strict On
Imports System.IO
Public Class Form1
Dim tim1 As New System.Windows.Forms.Timer
Dim tte4 As String = "afile.txt"
Private Sub Timer1_Tick(sender As Object, e As EventArgs)
tim1.Enabled = False
Dim target = Path.Combine("C:\temp", tte4)
If File.Exists(target) Then
Try
File.Delete(target)
Catch ex As Exception
MessageBox.Show(String.Format("The file ""{0}"" could not be deleted because {1}", target, ex.Message))
End Try
Else
' the file does not exist - do something if required
End If
End Sub
Private Sub SetUpTimer()
tim1.Enabled = False
tim1.Interval = 1000 * 15
' here we tie the event to the code
AddHandler tim1.Tick, AddressOf Timer1_Tick
End Sub
Private Sub Form1_Click(sender As Object, e As EventArgs) Handles MyBase.Click
' this has the side-effect of trying to delete a file after an interval
tim1.Enabled = True
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
SetUpTimer()
End Sub
End Class
You did not show code for creating the file - I assume you used a click on the form just to demonstrate the idea.

Using RFID in multiple windows forms

Hie there.
I am trying to use RFID tags on different forms. The code I have works fine if in one from. As soon you add it to another form it stops. I have tried using event handlers to no success. Does anyone know how I coud do this.
Here is my code:
Public Class Form1
Dim WithEvents phidgetRFID As Phidgets.RFID
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'To reduce code complexity we assume that there is one PhidgetRFID
'attached to the PC before the program is run.
phidgetRFID = New Phidgets.RFID()
phidgetRFID.Open()
'Defaults for text fields
txtStatus.Text = "Not Connected"
End Sub
Public Sub New()
' This call is required by the Windows Form Designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
lvPhidgetInfo.MultiColumn = False
lvPhidgetInfo.Items.Insert(0, "TagID Count")
End Sub
Private Sub phidgetRFID_Attach(ByVal sender As Object, ByVal e As Phidgets.Events.AttachEventArgs) Handles phidgetRFID.Attach
'When the Phidget RFID attaches update the form text boxes
Label1.Text = "Phidget RFID Reader has Attached"
txtStatus.Text = "Connected"
txtNumOutputs.Text = phidgetRFID.outputs.Count
txtSerialNumber.Text = (Str(phidgetRFID.SerialNumber))
phidgetRFID.Antenna = True
CheckBox3.Checked = True
phidgetRFID.LED = True
CheckBox2.Checked = True
End Sub
Private Sub phidgetRFID_Detach(ByVal sender As Object, ByVal e As Phidgets.Events.DetachEventArgs) Handles phidgetRFID.Detach
'If the Phidget RFID detaches close the form
Me.Close()
End Sub
Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
'If the form closes and the Phidget RFID is attached Close it.
If phidgetRFID.Attached = True Then
phidgetRFID.close()
End If
End Sub
Private Sub phidgetRFID_Error(ByVal sender As Object, ByVal e As Phidgets.Events.ErrorEventArgs) Handles phidgetRFID.Error
'If the Phidget RFID is not attached when the form opens show
'message box and close the form
MessageBox.Show(e.Description)
Me.Close()
End Sub
Private Sub phidgetRFID_RFIDTag(ByVal sender As Object, ByVal e As Phidgets.Events.TagEventArgs) Handles phidgetRFID.Tag
Static count As Integer
count = count + 1
lvPhidgetInfo.BeginUpdate()
lvPhidgetInfo.Items.Insert(1, e.Tag & " " & count)
lvPhidgetInfo.EndUpdate()
End Sub
Private Sub phidgetRFID_RFIDTagLost(ByVal sender As Object, ByVal e As Phidgets.Events.TagEventArgs) Handles phidgetRFID.TagLost
lvPhidgetInfo.BeginUpdate()
lvPhidgetInfo.Items.Insert(1, e.Tag & " Lost")
lvPhidgetInfo.EndUpdate()
End Sub
I have also tried this and I crushes without reporting an error.
Dim PhidgetRFID As New Phidgets.RFID()
AddHandler PhidgetRFID.Attach, AddressOf rfid_Attach
AddHandler PhidgetRFID.Detach, AddressOf rfid_Detach
AddHandler PhidgetRFID.Tag, AddressOf rfid_Tag
AddHandler PhidgetRFID.TagLost, AddressOf rfid_TagLost
AddHandler PhidgetRFID.Error, AddressOf rfid_Error
PhidgetRFID.open()
PhidgetRFID.waitForAttachment(3000)
Private Sub rfid_Attach(sender As Object, e As AttachEventArgs)
Label3.Text = "RFID reader {0} attached!" & e.Device.SerialNumber.ToString()
'Throw New NotImplementedException
End Sub
Private Sub rfid_Detach(sender As Object, e As DetachEventArgs)
Label4.Text = "RFID reader {0} ditached!" & e.Device.SerialNumber.ToString()
End Sub
Private Sub rfid_Tag(sender As Object, e As TagEventArgs)
Try
txtCardNumber.Text = e.Tag
Dim lastRFIDTag As String = txtCardNumber.Text
phidgetRFID.LED = True
' Throw New NotImplementedException
Catch ex As Exception
MsgBox("" & ex.Message, , "")
End Try
End Sub
Private Sub rfid_TagLost(sender As Object, e As TagEventArgs)
'Throw New NotImplementedException
phidgetRFID.LED = False
End Sub
Plaese help..

VB.Net - Updating progress bar from background worker

I am trying to build a log parser in VB.Net that will take IIS logs and insert them into a database. Having never really made a full out desktop application, I'm hitting a number of stumbling blocks, so please forgive me if my questions a very uninformed; I'm learning to walk while running.
The code that I'm working with looks like this:
Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyBase.OnLoad(e)
BackgroundWorker1.RunWorkerAsync()
End Sub
Private Sub BackgroundWorker1_DoWork(ByVal sender As System.Object, ByVal e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim logfile = "C:\ex111124.log"
Dim FileLength As Long = New System.IO.FileInfo(logfile).Length
logFileLabel.Text = logfile
Dim objReader As New System.IO.StreamReader(logfile)
Do While objReader.Peek() <> -1
OngoingLog.AppendText(objReader.ReadLine)
'BackgroundWorker1.ReportProgress(e.percentProgress)
Loop()
objReader.Close()
objReader = Nothing
End Sub
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
'Me.crunchingProgress.Value = e.ProgressPercentage
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
Close()
End Sub
So the function works, when this window is opened it starts to read the log file and updates a textbox with all of the rows currently read, but I also want it to update a progress bar in my main thread called crunchingProgress.
Any help would be greatly appreciated.
This should do the trick:
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Invoke(Sub()
Me.crunchingProgress.Value = e.ProgressPercentage
End Sub)
End Sub
You don't set the BackgroundWorker to report progress (WorkerReportsProgress)
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.RunWorkerAsync()
Now your BackgroundWorker1_ProgressChanged will be called in the context of the UI Thread and you can set the progressbar value
Of course, when you call ReportProgress to raise the ProgressChanged event, you need to pass the percentage of work done so far.
Using objReader As New System.IO.StreamReader(logfile)
Do While objReader.Peek() <> -1
Dim line = objReader.ReadLine()
OngoingLog.AppendText(line)
Dim pct = Convert.ToInt32((100 * line.Length) / FileLength )
BackgroundWorker1.ReportProgress(pct)
Loop
End Using