Wait X seconds between ReadLine of text file - vb.net

What I want is for the app to read the first line from a text file and output it to the textReply textbox, then wait for X time before reading and outputting the next line.
Dim fileIn As New System.IO.StreamReader("C:\test.txt")
Dim strData As String = ""
While (Not (fileIn.EndOfStream))
strData = fileIn.ReadLine()
textReply.Text = textReply.Text & strData & vbCr
System.Threading.Thread.Sleep(1000)
End While
As you can see I have tried sleeping the thread (not ideal as I want the app to stay responsive but thats another matter!) but every time it reads the whole file and dumps the lot into the textbox.
The reason is eventually it will be used for a serial connection to a device which needs time for the data transfer to the device and for the device to respond to each line sent.

You can create a Queue of string add each line to the list as you are reading the file, then you can poll it with a timer and add them to your textbox one at a time.
Public Class Form1
Dim myQueue As Queue(Of String) = New Queue(Of String)
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
Dim fileIn As New System.IO.StreamReader("C:\test.txt")
Timer1.Start()
Dim strData As String = ""
While (Not (fileIn.EndOfStream))
strData = fileIn.ReadLine()
myQueue.Enqueue(strData)
End While
End Sub
Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
If myQueue.Count > 0 Then
textReply.Text += myQueue.Dequeue & vbCrLf
End If
End Sub
End Class

Related

Loop through each item in Listbox and load a webpage

I'm trying to load a new webpage through the control webview2 in .net6+ windows forms, and I'm using a listbox to extract any single item and add it to the url to load on webview.
For example in listbox I have:
11
22
33
44
55
I would like at the press of a button that a loop starts to load one by one,each of these items like
WebView21.Source = New Uri("https://google.it" & ListBox1.Items.first & "rest of the url")
and after the webpage is loaded, it s supposed to extract it's html to check if a certain string is present with
Dim html As String
html = Await WebView21.ExecuteScriptAsync("document.documentElement.outerHTML;")
If html.Contains("Not found") Then
MsgBox("In Vacanza")
Else
MsgBox("Attivo")
End If
End Sub
after that, it goes back to the second listbox item, load the webview, check the html and so on.
My question is how can I loop the WebView in order to pick each of the items one by one and to proceed to do these little things in the while?
p.s. Once the loop arrives to the last listbox item, would it be possible to start it again from the first item?
Much thanks
edit1:
I'm trying with
Private ReadOnly resetEvent As New ManualResetEvent(False)
Async Sub scanWeb()
For Each listBoxElem As String In ListBox1.Items
resetEvent.Reset()
AddHandler WebView2.CoreWebView2.NavigationCompleted, AddressOf OnNavigationCompleted
WebView2.Source = New Uri("https://ikalogs.ru/tools/map/?page=1&server=22&world=10&state=active&search=city&allies%5B1%5D=&allies%5B2%5D=&allies%5B3%5D=&allies%5B4%5D=&nick=" & listBoxElem & "&ally=&island=&city=&x=&y=")
Await Task.Run(Sub() resetEvent.WaitOne())
RemoveHandler WebView2.CoreWebView2.NavigationCompleted, AddressOf OnNavigationCompleted
Dim html As String
html = Await WebView2.ExecuteScriptAsync("document.documentElement.outerHTML;")
If html.Contains("Not found") Then
DataGridView1.Rows.Add(listBoxElem, "IN vacanza")
Else
DataGridView1.Rows.Add(listBoxElem, "Attivo")
End If
Next
End Sub
Private Sub OnNavigationCompleted(sender As Object, e As CoreWebView2NavigationCompletedEventArgs)
resetEvent.Set()
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
WebView2 = New WebView2()
WebView2.EnsureCoreWebView2Async()
End Sub
but it seems the loop doesn't wait for the process to end and it goes straight to the next listbox item...
best way is to enumerate listBox items in a for-each loop : (I added an escape way -simple mouse clic on form - to quit loop)
Dim wStop As Boolean
sub scanWeb()
Do
For Each listBoxElem As String In ListBox1.Items
WebView21.Source = New Uri("https://google.it" & listBoxElem & "rest of the url")
'etc....
Next
Loop Until wStop = True
wStop = False
end sub
'way to stop scan
Private Sub form_clic(sender As Object, e As MouseEventArgs) Handles MyBase.MouseClick
wStop = True
End Sub
*** Update *****
The webview2 control is rather made to display data, and I have no experience on it.
Also I suggest you use a simpler method, based on System.Net.WebClient() and associated with threading.
Here is a start of code that works:
Dim wStop As Boolean
Dim i As Integer = 0
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim BackProcess = New Thread(Sub() Me.scanWeb())
BackProcess.Priority = ThreadPriority.Normal
BackProcess.Start()
end sub
Sub scanWeb()
Do While Not wStop
Dim WC As New System.Net.WebClient()
'change url for your need
Dim url As String = "https://www.google.fr/search?q=" & ListBox1.Items(i)
Dim s As System.IO.Stream = WC.OpenRead(url)
Dim sr As New System.IO.StreamReader(s)
Dim html As String = sr.ReadToEnd()
If html.Contains("Not found") Then 'beginInvoke allows the back task to communicate with UI
Me.BeginInvoke(Sub() DataGridView1.Rows.Add(ListBox1.Items(i), "In vacanza"))
Else
Me.BeginInvoke(Sub() DataGridView1.Rows.Add(ListBox1.Items(i), "Attivo"))
End If
i += 1
If i > ListBox1.Items.Count - 1 Then i = 0
Loop
End Sub
'button event to stop scanning
Private Sub stopScan_Click(sender As Object, e As EventArgs) Handles stopScan.Click
wStop = True
End Sub

How do I output from a PowerShell script (PSObjects?) to a WinForms TextBox in real-time?

I am executing a PowerShell script from a Visual Basic WinForms UI, and I managed to code it so it executes on a BackgroundWorker thread so that the UI doesn't lock up while the script is running. What it does is imports a .printerExport file to add printers and drivers to a target host, and it runs great. The only issue is that I set the output of the PSObjects to a TextBox and they all get output at the end once the script is completed, rather than output in real time like it would in a PowerShell console window.
I have tried multiple things to get it to output in real-time, but I am out of ideas, and even ReportProgress doesn't manage to get the real-time output as well.
How can I get this done? Below are the three involved Sub functions I am currently using:
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim HostName As String
Dim PrintName As String
Dim SourceFilePath As String
Dim DestinationPath As String
Dim FileName As String
HostName = txtHostName.Text
PrintName = "\\" & txtHostName.Text
SourceFilePath = txtFilePath.Text
DestinationPath = PrintName & "\c$\Temp\"
If String.IsNullOrEmpty(HostName) Or String.IsNullOrEmpty(SourceFilePath) Then
MessageBox.Show("Please enter the target host name and choose a file path.")
Return
End If
FileName = Path.GetFileName(SourceFilePath)
File.Copy(SourceFilePath, Path.Combine(DestinationPath, Path.GetFileName(SourceFilePath)), True)
Dim PsEnv As New RunspaceInvoke
Dim App As String = $"Invoke-Command -ComputerName {HostName} {{C:\Windows\System32\spool\tools\Printbrm.exe -r -s {PrintName} -f ""C:\Temp\{FileName}""}}"
Dim AppObjects As Collection(Of PSObject) = PsEnv.Invoke(App)
Dim Output As New StringBuilder()
Dim id As Integer = 0
For Each psobj2 As PSObject In AppObjects
id += 1
BackgroundWorker1.ReportProgress(id, psobj2.ToString() & vbCrLf & vbCrLf)
Next
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
Dim userState As String = CType(e.UserState, String)
TextBox3.AppendText(userState)
End Sub
Private Sub buttonInstall_Click(sender As Object, e As EventArgs) Handles buttonInstall.Click
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.RunWorkerAsync()
End Sub

How can I run two tasks concurrently?

I have this for/next loop where I download a file and then process and load its contnets into a data base:
For Each f As WinSCP.RemoteFileInfo In remotefilesinf
If DownloadFile(FTPSession, CacheDirPath, "/mnt/usb", f) Then
LoadDB(System.IO.Path.Combine(CacheDirPath, f.Name))
Else
MsgBox("Download failed.")
End If
Next
In order to speed things up, how can I do the DB loading while the next file is downloading? I cannot do the DBLoad until each file download is complete and I can only do one DBLoad task at a time due to locking of the database.
I tried using a background worker for the LoadDB task but the RunWorkerCompleted event will not fire while the UI thread is busy with the download so i do not know when I can do the next DBload (DB not locked).
Any advice appreciated.
Here is another try since the requirement for the question have changed:
Public Class Form1
Shared rnd As New Random
Private download_que As New Queue(Of String)
Private process_que As New Queue(Of String)
Private download_thread As Thread
Private process_thread As Thread
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
download_que.Enqueue("File 1.db")
download_que.Enqueue("File 2.db")
download_que.Enqueue("File 3.db")
download_que.Enqueue("File 4.db")
download_que.Enqueue("File 5.db")
download_que.Enqueue("File 6.db")
If download_thread Is Nothing then
download_thread = New Thread(AddressOf DownloadFiles)
download_thread.Start()
End If
End Sub
Private AppendTextCaller As New Action(Of TextBox, String)(AddressOf AppendText)
Public Sub AppendText(ByVal control As TextBox, ByVal text As String)
control.AppendText(text)
End Sub
Public Sub DownloadFiles()
Dim file As String
While download_que.Count > 0
SyncLock download_que
file = download_que.Dequeue()
End SyncLock
Dim path As String = Download(file)
SyncLock process_que
process_que.Enqueue(path)
End SyncLock
If process_thread Is Nothing Then
process_thread = New Thread(AddressOf ProcessFiles)
process_thread.Start()
End If
End While
download_thread = Nothing
End Sub
Public Sub ProcessFiles()
Dim path As String, ok As Boolean
ok = True
While process_que.Count > 0 And ok
SyncLock process_que
path = process_que.Dequeue()
End SyncLock
ok = LoadDB(path)
End While
process_thread = Nothing
End Sub
Public Function Download(ByVal filename As String) As String
Dim sw = Stopwatch.StartNew()
Me.Invoke(AppendTextCaller, TextBox1, filename)
Thread.Sleep(1500 + 500*rnd.Next(15))
Dim message As String = String.Format(" ({0:F1} sec)", sw.ElapsedMilliseconds / 1000)
Me.Invoke(AppendTextCaller, TextBox1, message)
Me.Invoke(AppendTextCaller, TextBox1, Environment.NewLine)
Return IO.Path.Combine(IO.Path.GetTempPath(), filename)
End Function
Public Function LoadDB(ByVal path As String) As Boolean
Dim sw = Stopwatch.StartNew()
Dim filename = IO.Path.GetFileName(path)
Me.Invoke(AppendTextCaller, TextBox2, filename)
Thread.Sleep(800 + 500*rnd.Next(6))
Dim message As String = String.Format(" ({0:F1} sec)", sw.ElapsedMilliseconds / 1000)
Me.Invoke(AppendTextCaller, TextBox2, message)
Me.Invoke(AppendTextCaller, TextBox2, Environment.NewLine)
Return True
End Function
End Class
What about using two backgroundworkers? Use one to download files, the other one to stuff them into the db.
If a download completes, append the file to a list and each time a db update finishes, look at that list from bgw2...
You can run the DBLoad on a thread and set a ManualResetEvent to stop the execution before lauching the new DBLoad thread until the other ona finished.
Dim locker as New ManualResetEvent(True)
The locker acts like a traffic light, stoping the execution and waiting when is marked and going throught when otherwise.
Block the locker anywhere with:
locker.Reset()
Unblock the locker anywhere with:
locker.Set()
Set a stoping spot:
locker.WaitOne()
To see full capabilities see MSDN.
I think this is what you want:
Public Function DownLoadFile(ByVal f As String) As String
Trace.WriteLine("Start Downloading " & f)
Dim x As Integer = ProgressBar1.Value
Threading.Thread.Sleep(2000)
Me.Invoke(SetProgressCaller, x + 25)
Trace.WriteLine("Done Downloading " & f)
Return IO.Path.Combine(IO.Path.GetTempPath(), f)
End Function
Public Sub LoadDB(ByVal f As String)
Trace.WriteLine("Start Loading " & f)
Dim x As Integer = ProgressBar1.Value
Threading.Thread.Sleep(1000)
Me.Invoke(SetProgressCaller, x + 25)
Trace.WriteLine("Done Loading " & f)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
ProgressBar1.Value = 0
Dim f_path_1 = DownLoadFile("File 1")
Dim t1 As New Threading.Thread(AddressOf LoadDB)
t1.Start(f_path_1)
ProgressBar1.Value = 50
Dim f_path_2 = DownLoadFile("File 2")
Dim t2 As New Threading.Thread(AddressOf LoadDB)
t2.Start(f_path_2)
End Sub
' Can be called using Form.Invoke() from any thread
Private SetProgressCaller As New Action(Of Integer)(AddressOf SetProgress)
' Set progress bar in main thread
Public Sub SetProgress(ByVal pct As Integer)
ProgressBar1.Value = pct
End Sub
with the results:
Start Downloading File 1
Done Downloading File 1
Start Downloading File 2
Start Loading C:\Users\#####\AppData\Local\Temp\File 1
Done Loading C:\Users\#####\AppData\Local\Temp\File 1
Done Downloading File 2
Start Loading C:\Users\#####\AppData\Local\Temp\File 2
Done Loading C:\Users\#####\AppData\Local\Temp\File 2
which translates to
Downloading of file 1 (takes 2 sec)
Loading file 1 into DB (takes 1 sec) AND
Downloading of file 2 (takes 2 sec)
Loading of file 2 into DB (takes 1 sec)

Iteration of a text file in Visual Basic

Ok, so I'm making a program that will read from a text file, convert each line into an array, the. Send each of those lines 1 per tick. This is in 2010 Visual Basic.
Closest I've gotten is sending all at once, I worked on it over night and am slowly destroying it.
Ideally, I want Button 1 click to populate the array from the file at LocationTB then start the timer. The timer should send a line at a time on the GapTB interval.
Public Class Form1
Public TextLine As String
Public MyFileName As String
Public MyNewLine(1000) As String
Private Property z As Integer
Private Property objReader As Object
Public Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If RadioButton2.Checked = True Then
Dim Textline As String = ""
Dim FILE_NAME As String = LocationTB.Text
Dim objReader As New System.IO.StreamReader(FILE_NAME)
MyFileName = LocationTB.Text
FileOpen(1, MyFileName, OpenMode.Input, OpenAccess.Read, OpenShare.Shared)
z = 0
Do Until EOF(1)
MyNewLine(z) = LineInput(1)
z = z + 1
Loop
FileClose(1)
End If
Timer1.Interval = GapTB.Text
Timer1.Enabled = True
End Sub
Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
If RadioButton1.Checked = True Then
AppActivate(Hook.Text)
SendKeys.Send(SimpleTB.Text)
SendKeys.Send((Chr(13)))
ElseIf RadioButton2.Checked = True Then
For Each
TextLine = TextLine & objReader.ReadLine
AppActivate(Hook.Text)
SendKeys.Send(TextLine)
SendKeys.Send((Chr(13)))
Next
Else
MsgBox("File Does Not Exist")
End If
End Sub
Is this the kind of thing you're looking for?
It'll write the contents of a file (in this instance "C:\mark.txt") to the output window in Visual Studio.
Public Class Form1
Private myTimer As Timer
Private lines As String()
Private currentLine As Integer
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
lines = IO.File.ReadAllLines("C:\mark.txt")
currentLine = 0
myTimer = New Timer()
AddHandler myTimer.Tick, AddressOf myTimer_Tick
myTimer.Interval = 1000
myTimer.Enabled = True
End Sub
Private Sub myTimer_Tick(sender As Object, e As EventArgs)
If currentLine < lines.Count Then
Dim lineToSend As String = lines(currentLine)
Debug.Print(lineToSend)
currentLine += 1
Else
myTimer.Enabled = False
End If
End Sub
End Class
Caveat
The above code isn't scalable. If you're SURE the file will always be small then it will do (that said, they're never always small).
To make this scalable you'd need to hold the file open, and read each line as you need it, not load the entire file contents at once.
I know how to do it to a application I started myself: Here's the code to make Calculator work:
Dim ProcID As Integer
' Start the Calculator application, and store the process id.
ProcID = Shell("CALC.EXE", AppWinStyle.NormalFocus)
' Activate the Calculator application.
AppActivate(ProcID)
' Send the keystrokes to the Calculator application.
My.Computer.Keyboard.SendKeys("22", True)
My.Computer.Keyboard.SendKeys("*", True)
My.Computer.Keyboard.SendKeys("44", True)
My.Computer.Keyboard.SendKeys("=", True)
' The result is 22 * 44 = 968.
AppActivate should know the exact title of the form to activate. If not known, iterate the Processes to locate yours. If the title is correctly specified it should work

Read Text File and Output Multiple Lines to a Textbox

I'm trying to read a text file with multiple lines and then display it in a textbox. The problem is that my program only reads one line. Can someone point out the mistake to me?
Imports System.IO
Imports Microsoft.VisualBasic.FileIO
Public Class Form1
Private BagelStreamReader As StreamReader
Private PhoneStreamWriter As StreamWriter
Dim ResponseDialogResult As DialogResult
Private Sub OpenToolStripMenuItem_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles OpenToolStripMenuItem.Click
'Dim PhoneStreamWriter As New StreamWriter(OpenFileDialog1.FileName)
'Is file already open
If PhoneStreamWriter IsNot Nothing Then
PhoneStreamWriter.Close()
End If
With OpenFileDialog1
.InitialDirectory = Directory.GetCurrentDirectory
.FileName = OpenFileDialog1.FileName
.Title = "Select File"
ResponseDialogResult = .ShowDialog()
End With
'If ResponseDialogResult <> DialogResult.Cancel Then
' PhoneStreamWriter = New StreamWriter(OpenFileDialog1.FileName)
'End If
Try
BagelStreamReader = New StreamReader(OpenFileDialog1.FileName)
DisplayRecord()
Catch ex As Exception
MessageBox.Show("File not found or is invalid.", "Data Error")
End Try
End Sub
Private Sub DisplayRecord()
Do Until BagelStreamReader.Peek = -1
TextBox1.Text = BagelStreamReader.ReadLine()
Loop
'MessageBox.Show("No more records to display.", "End of File")
'End If
End Sub
Private Sub SaveToolStripMenuItem_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles SaveToolStripMenuItem.Click
With SaveFileDialog1
.InitialDirectory = Directory.GetCurrentDirectory
.FileName = OpenFileDialog1.FileName
.Title = "Select File"
ResponseDialogResult = .ShowDialog()
End With
PhoneStreamWriter.WriteLine(TextBox1.Text)
With TextBox1
.Clear()
.Focus()
End With
TextBox1.Clear()
End Sub
Private Sub ExitToolStripMenuItem_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles ExitToolStripMenuItem.Click
Dim PhoneStreamWriter As New StreamWriter(OpenFileDialog1.FileName)
PhoneStreamWriter.Close()
Me.Close()
End Sub
End Class
Here is a sample textfile:
Banana nut
Blueberry
Cinnamon
Egg
Plain
Poppy Seed
Pumpkin
Rye
Salt
Sesame seed
You're probably only getting the last line in the file, right? Your code sets TextBox1.Text equal to BagelSteramReader.ReadLine() every time, overwriting the previous value of TextBox1.Text. Try TextBox1.Text += BagelStreamReader.ReadLine() + '\n'
Edit: Though I must steal agree with Hans Passant's commented idea on this; If you want an more efficient algorithm, File.ReadAllLines() even saves you time and money...though I didn't know of it myself. Darn .NET, having so many features...
I wrote a program to both write to and read from a text file. To write the lines of a list box to a text file I used the following code:
Private Sub txtWriteToTextfile_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txtWriteToTextfile.Click
Dim FileWriter As StreamWriter
FileWriter = New StreamWriter(FileName, False)
' 3. Write some sample data to the file.
For i = 1 To lstNamesList.Items.Count
FileWriter.Write(lstNamesList.Items(i - 1).ToString)
FileWriter.Write(Chr(32))
Next i
FileWriter.Close()
End Sub
And to read and write the contents of the text file and write to a multi-line text box (you just need to set the multiple lines property of a text box to true) I used the following code. I also had to do some extra coding to break the individual words from the long string I received from the text file.
Private Sub cmdReadFromTextfile_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdReadFromTextfile.Click
Dim sStringFromFile As String = ""
Dim sTextString As String = ""
Dim iWordStartingPossition As Integer = 0
Dim iWordEndingPossition As Integer = 0
Dim iClearedTestLength As Integer = 0
Dim FileReader As StreamReader
FileReader = New StreamReader(FileName)
sStringFromFile = FileReader.ReadToEnd()
sTextString = sStringFromFile
txtTextFromFile.Text = ""
Do Until iClearedTestLength = Len(sTextString)
iWordEndingPossition = CInt(InStr((Microsoft.VisualBasic.Right(sTextString, Len(sTextString) - iWordStartingPossition)), " "))
txtTextFromFile.Text = txtTextFromFile.Text & (Microsoft.VisualBasic.Mid(sTextString, iWordStartingPossition + 1, iWordEndingPossition)) & vbCrLf
iWordStartingPossition = iWordStartingPossition + iWordEndingPossition
iClearedTestLength = iClearedTestLength + iWordEndingPossition
Loop
FileReader.Close()
End Sub