Wait for batch file to close before continuing - VB.net - vb.net

I'm trying to run a batch file via VB and I need to wait for it to complete/exit before progressing. The issue I believe I am having is that when a batch file is executed, it opens cmd.exe and not the batch file.
This is what I am executing with VB
My.Computer.FileSystem.DeleteFile(My.Application.Info.DirectoryPath & "\PingCheck\machines.txt")
FileCopy(My.Application.Info.DirectoryPath & "\machines.txt", My.Application.Info.DirectoryPath & "\PingCheck\machines.txt")
Dim psi As New ProcessStartInfo(My.Application.Info.DirectoryPath & "\PingCheck\go.bat")
psi.RedirectStandardError = True
psi.RedirectStandardOutput = True
psi.CreateNoWindow = False
psi.WindowStyle = ProcessWindowStyle.Hidden
psi.UseShellExecute = False
Dim process As Process = process.Start(psi)
process.WaitForExit()
ProgressBar1.Value = ProgressBar1.Value + 2
FileCopy(My.Application.Info.DirectoryPath & "\PingCheck\machines.txt", My.Application.Info.DirectoryPath & "\machines.txt")
'My.Computer.FileSystem.DeleteFile(My.Application.Info.DirectoryPath & "\ping.bat")
MsgBox("Ping Check Complete")
The problem im having is that it will just delete ping.bat before it completes.
How do I go about monitoring the process from the batch file I call. Then once it exits, continue with the script?

RHicke shows a nice example of how to run a batch process in VB.NET here, Run batch file in vb.net?.
To expand, you should use the function WaitForExit() to wait for the process to complete.
Dim psi As New ProcessStartInfo("Path TO Batch File")
psi.RedirectStandardError = True
psi.RedirectStandardOutput = True
psi.CreateNoWindow = False
psi.WindowStyle = ProcessWindowStyle.Hidden
psi.UseShellExecute = False
Dim process As Process = Process.Start(psi)
process.WaitForExit()

You could use the System.Diagnostics.Process class to start the batch file. The process reference will give you access to the property HasExited (and more interesting information). The HasExited property indicates whether a process has completed.
var process = System.Diagnostics.Process.Start(new ProcessStartInfo
{
FileName = "batch file path",
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false,
Arguments = "parameters if applicable",
CreateNoWindow = true
});
while(!process.HasExited)
{
// obviously do some clever here to wait
}
Code is in C# but the principle should work in VB.NET

I've done something similar before. This code invokes RoboCopy from within VB.Net, using the System.Diagnostics.Process (mentioned by rivethead_).
Dim proc As System.Diagnostics.Process = New System.Diagnostics.Process()
proc.EnableRaisingEvents = False
proc.StartInfo.FileName = "d:\robocopy\robocopy"
proc.StartInfo.Arguments = strSrcDir & " " & strDestDir & " " & strFile & " " & My.Settings.robocopyFlags
proc.Start()
proc.WaitForExit()
Otherwise, what is the ping.bat doing? Is it just doing a "ping" command? If so, maybe you could invoke that with a System.Diagnostics.Process (instead of invoking a .bat file to do it). That might give you some more control over your process.

Related

Using cmd in VB, using commands and receiving output

I need to know, if you could help me, how to insert commands in vb then they run in cmd and i get the output.
I need to do "net localgroup Administradores a58465 /add" and get the error message if there is one.
Solution: `Dim myProcess As Process = New Process
Dim s As String
myProcess.StartInfo.FileName = "c:\windows\system32\cmd.exe"
myProcess.StartInfo.UseShellExecute = False
myProcess.StartInfo.CreateNoWindow = True
myProcess.StartInfo.RedirectStandardInput = True
myProcess.StartInfo.RedirectStandardOutput = True
myProcess.StartInfo.RedirectStandardError = True
myProcess.Start()
Dim sIn As System.IO.StreamWriter = myProcess.StandardInput
Dim sOut As System.IO.StreamReader = myProcess.StandardOutput
Dim sErr As System.IO.StreamReader = myProcess.StandardError
'sIn.AutoFlush = True
sIn.Write("cls" & System.Environment.NewLine)
sIn.Write("net user" & System.Environment.NewLine)
sIn.Write("exit" & System.Environment.NewLine)
s = sOut.ReadToEnd()
If Not myProcess.HasExited Then
myProcess.Kill()
End If
LB1.Text = s
LB1.Visible = True
sIn.Close()
sOut.Close()
sErr.Close()
myProcess.Close()`
Check out Process.Start. http://msdn.microsoft.com/en-us/library/0w4h05yb(v=vs.110).aspx
Also look for the ProcessStartInfo class, which will give you options on how to kick off an external process.
Console input and output can be made available to your program through ProcessStartInfo.

How do i read the output from a batch file in a vb program

thanks in advance i just want to know if theres a way to read the output from a running batch file in a vb.net program. Thanks!
As one commenter above mentions, you can run the batch file in a shell within your VB.NET program and then read the directed output. I have done this exactly in previous project.
Here is a code snippet which shows how you can do it:
Dim outputFile As String = """" & Path.GetTempFileName & """"
Dim batchCommand As String = """C:\Path\To\MyFile.bat"">" & outputFile
Dim cmdProcess As New Process
With cmdProcess
.StartInfo = New ProcessStartInfo("cmd.exe", "/C " & batchCommand)
With .StartInfo
.CreateNoWindow = True
.UseShellExecute = False
End With
.Start()
.WaitForExit()
End With
' This is the output from the batch file.
Dim batchOutput As String = My.Computer.FileSystem.ReadAllText(outputFile)

Displaying the output of Process.Start

I have a Process.Start command that I would like to see the output of, but the new window is opening and closing too quickly for me to see anything. Here is the code I have so far that I'm working with:
System.Diagnostics.Process.Start(Environment.GetEnvironmentVariable("VS110COMNTOOLS") & "..\Ide\MSTEST.EXE", "/Testsettings: """ & rwSettings & "" & " /Testcontainer: """ & rwContainer & "" & " /Resultsfile: """ & rwResults & "")
Unfortunately as I try to debug this if I allow this to run it flashes up the window but doesn't let me see what the error is, or if it's running successfully at all. I'm using VS2012 so I might just not be looking at the right view when I'm debugging.
Here is some code taen out of the middle of some logic, so it is not standalone. You can use ProcessStartInfo() and Process() to have more control:
Dim start_info As New ProcessStartInfo("sqlcmd", cmd)
start_info.UseShellExecute = False
start_info.CreateNoWindow = True
start_info.RedirectStandardOutput = True
start_info.RedirectStandardError = True
' Make the process and set its start information.
Dim proc As New Process()
proc.StartInfo = start_info
Dim dt As Date = Now()
' Start the process.
proc.Start()
' Attach to stdout and stderr.
Dim std_out As StreamReader = proc.StandardOutput() ' will not continue until process stops
Dim std_err As StreamReader = proc.StandardError()
' Retrive the results.
Dim sOut As String = std_out.ReadToEnd()
Dim sErr As String = std_err.ReadToEnd()

How to run batch commands with dynamic statements?

I am writing code in VB.NET 2.0 and want to run batch commands for FTP using FTP -s:filename command.
I have a Batch file FTP.TXT for FTP Upload. It has the following statements:
OPEN <FPT SERVER IP>
USERNAME
PASSWORD
ASC
CD FOLDERNAME
PUT D:\DRFT000009.TXT FTPDRFTIN.DRFT000009
BYE
I have to dynamically change the filename in the Batch File. Now either I can create a Batch file at runtime and then read it or I got a code to set the input stream of the Process object. But its not working as desired.
This code runs fine but here I read a static batch file FTP.TXT from the computer:
Public Sub FTP4()
Dim psi As ProcessStartInfo
Dim totalerror As String = ""
psi = New ProcessStartInfo()
psi.FileName = "FTP.EXE"
psi.Arguments = " -s:D:\FTP.TXT"
psi.RedirectStandardError = True
psi.RedirectStandardOutput = True
psi.CreateNoWindow = True
psi.WindowStyle = ProcessWindowStyle.Hidden
psi.UseShellExecute = False
Dim process As Process = process.Start(psi)
Dim error2 As String = process.StandardError.ReadToEnd()
totalerror = totalerror & error2
process.WaitForExit()
Response.Write(totalerror)
End Sub
But I want somehow to get the FTP done with custom file name for each request. This is what I tried which is not working:
Public Sub FTP5()
Dim totalerror As String = ""
Dim BatchScriptLines(6) As String
Dim process As New Process
process.StartInfo.FileName = "FTP.EXE"
process.StartInfo.UseShellExecute = False
process.StartInfo.CreateNoWindow = True
process.StartInfo.RedirectStandardInput = True
process.StartInfo.RedirectStandardOutput = True
process.StartInfo.RedirectStandardError = True
process.Start()
process.BeginOutputReadLine()
Using InputStream As System.IO.StreamWriter = process.StandardInput
InputStream.AutoFlush = True
BatchScriptLines(0) = "OPEN <FPT IP ADDRESS>"
BatchScriptLines(1) = "USERNAME"
BatchScriptLines(2) = "PASSWORD"
BatchScriptLines(3) = "ASC"
BatchScriptLines(4) = "CD SFCD40DAT"
BatchScriptLines(5) = "PUT D:\DRFT000006.TXT FTPDRFTIN.DRFT000006"
BatchScriptLines(6) = "BYE"
For Each ScriptLine As String In BatchScriptLines
InputStream.Write(ScriptLine & vbCrLf)
Next
End Using
Dim error2 As String = process.StandardError.ReadToEnd()
totalerror = totalerror & error2
process.WaitForExit()
Response.Write(totalerror)
End Sub
Please advise how I can get the "FTP -s:filename" command executed in this case. Basically I want to do something similar to single line batch file execution which I not able to do.
Being a simple text file with a clear format, you could rewrite the file passing the parameters that need to be dynamically changed
Public Sub FTP4()
' Of course I assume that, at this point your program knows the exact values '
' to pass at the procedure that rebuilds the file'
PrepareFTPFile("D:\DRFT000006.TXT", "USERNAME", "PASSWORD")
' Now you call the original code.....
Dim psi As ProcessStartInfo
Dim totalerror As String = ""
psi = New ProcessStartInfo()
psi.FileName = "FTP.EXE"
psi.Arguments = " -s:D:\FTP.TXT"
....
End Sub
Public Sub PrepareFTPFile(fileToUpload as String, username as string, userpass as string)
Using sw = new StreamWriter("D:\FTP.TXT", False)
sw.WriteLine("OPEN <FPT IP ADDRESS>")
sw.WriteLine(username)
sw.WriteLine(userpass)
sw.WriteLine("ASC")
sw.WriteLine("CD SFCD40DAT")
sw.WriteLine("PUT " + fileToUpload + " FTPDRFTIN." + Path.GetFileNameWithoutExtension(fileToUpdload))
sw.WriteLine("BYE")
End Using
End Sub

VB.Net process exits as soon as I attempt to read standard output or error

Initial Problem
I apologize if this issue has been raised and addressed elsewhere; I searched this site, and Google at large without any luck.
I'm trying to write a simple VB.Net Windows Forms Application to allow a user to run the Windows File Compare program (fc.exe) with a very simple GUI ("browse" buttons to select files, checkboxes to select modifiers, and a textbox for the output).
The problem is that whenever I try to read the standard output or error from the process, it immediately stops, and nothing is output. I've verified that the process arguments are correct by setting "createnowindow" to False and not redirecting Output or Errors.
To see if the process is actually running or not, I put a "while" loop after proc.start:
Do While proc.HasExited = False
textbox.AppendText(i & vbNewLine)
i += 1
Loop
If the process runs normally, I get a count up to about 80 or 90. If I do anything at all with the standardoutput or standarderror, the textbox only shows the initial value of "0". By "anything at all", I mean assigning the proc.StandardOutput.ReadToEnd to a variable. If I use proc.StandardOutput.Peek, it returns a -1 and the loop remains at 0.
I've noticed that if I only redirect either Output or Error (but not both), and I enable the process to open a new window, the new window is empty and immediately exits (even if I'm not attempting to read the redirected stream in my code), whereas if neither is redirected, it displays a few pages of results, then exits. I don't know if this is normal, or if the File Compare executable is somehow mixing the Output and Error streams to generate its output, or if something like that is even possible.
I'm extremely new to coding in general (I've been working with VB.net for about a month, and that's the extent of my programming experience), so my I'm aware that my troubleshooting and assumptions may be completely off base, and I appreciate any assistance anyone can provide. As it is, I'm completely floundering, and my inexperience is making it difficult to look for alternatives (for instance, I can't figure out how to correctly handle asynchronous output). For reference, here's my embarrassingly clunky code as it currently stands:
Dim cmdinput As String = """" & file1path & """" & " " & """" & file2path & """"
Dim cmdmods As String = " "
Dim i As Integer = 0
Dim proc As New Process
proc.StartInfo.CreateNoWindow = True
proc.StartInfo.UseShellExecute = False
proc.StartInfo.FileName = "C:\Windows\System32\fc.exe"
proc.StartInfo.Arguments = cmdinput & cmdmods
proc.StartInfo.RedirectStandardOutput = True
proc.StartInfo.RedirectStandardError = True
proc.Start()
proc.StandardOutput.ReadToEnd()
Do While proc.HasExited = False
scanbox.AppendText(i & vbNewLine)
i += 1
Loop
Possible Solution
After Hans Passant pointed out that I should be seeing errors, if nothing else, I messed around with my code and was able to get a result, though a less than optimal one. Instead of running FC.exe directly, I ran CMD.exe. I had tried this before with no luck, but that's because CMD.exe doesn't accept "fc " as process.startinfo.arguments.
I passed the "fc " to cmd.exe with proc.standardinput.writeline(). At this point I was able to read CMD.exe's redirected output. I still have no idea why I can't directly read FC.exe, but this is a pretty good band-aid in the meantime. On the off chance that anyone else feels the need to add a GUI to a perfectly good command-line executable and runs into problems, here's my code:
Public Sub compare()
Dim cmdinput As String = "fc " & """" & file1path & """" & " " & """" & file2path & """"
Dim cmdmods As String = " "
Dim proc As New Process
proc.StartInfo.CreateNoWindow = True
proc.StartInfo.UseShellExecute = False
proc.StartInfo.FileName = "C:\Windows\System32\cmd.exe"
proc.StartInfo.Arguments = cmdinput & cmdmods
proc.StartInfo.RedirectStandardOutput = True
proc.StartInfo.RedirectStandardError = True
proc.StartInfo.RedirectStandardInput = True
proc.Start()
proc.StandardInput.WriteLine(cmdinput)
proc.StandardInput.Close()
scanbox.AppendText(proc.StandardOutput.ReadToEnd)
proc.WaitForExit()
proc.Close()
proc.Dispose()
End Sub
I Greatly appreciate the patience from Hans Passant and Dan Verdolino in offering suggestions to my rambling question. I've been hammering my head against a wall for a week trying to kludge together some way of doing this.
Solution
Instead of running FC.exe directly, I ran CMD.exe. I had tried this before with no luck, but that's because CMD.exe doesn't accept "fc (args)" as process.startinfo.arguments.
I passed the "fc (args)" to cmd.exe with proc.standardinput.writeline(). At this point I was able to read CMD.exe's redirected output. I still have no idea why I can't directly read FC.exe's output or errors, but this is a pretty good band-aid in the meantime. On the off chance that anyone else feels the need to add a GUI to a perfectly good command-line executable and runs into problems, here's my code:
Public Sub compare()
Dim cmdinput As String = "fc " & """" & file1path & """" & " " & """" & file2path & """"
Dim cmdmods As String = " "
Dim proc As New Process
proc.StartInfo.CreateNoWindow = True
proc.StartInfo.UseShellExecute = False
proc.StartInfo.FileName = "C:\Windows\System32\cmd.exe"
proc.StartInfo.Arguments = cmdinput & cmdmods
proc.StartInfo.RedirectStandardOutput = True
proc.StartInfo.RedirectStandardError = True
proc.StartInfo.RedirectStandardInput = True
proc.Start()
proc.StandardInput.WriteLine(cmdinput)
proc.StandardInput.Close()
scanbox.AppendText(proc.StandardOutput.ReadToEnd)
proc.WaitForExit()
proc.Close()
proc.Dispose()
End Sub
Private Delegate Sub InvokeWithString(ByVal text As String)
Public Sub StartFC()
Private psi As ProcessStartInfo
Private cmd As Process
Dim CMDINPUT As String = "fc " & """" & file1path & """" & " " & """" & file2path & """"
Dim FileToHit As String = "c:\windows\system32\fc.exe "
psi = New ProcessStartInfo(FileToHit)
Dim systemencoding As System.Text.Encoding = _
System.Text.Encoding.GetEncoding(Globalization.CultureInfo.CurrentUICulture.TextInfo.OEMCodePage)
With psi
.Arguments = CMDINPUT
.UseShellExecute = False ' Required for redirection
.RedirectStandardError = True
.RedirectStandardOutput = True
.RedirectStandardInput = True
.CreateNoWindow = True
.StandardOutputEncoding = systemencoding
.StandardErrorEncoding = systemencoding
End With
' EnableraisingEvents is required for Exited event
cmd = New Process With {.StartInfo = psi, .EnableRaisingEvents = True}
AddHandler cmd.ErrorDataReceived, AddressOf Async_Data_Received
AddHandler cmd.OutputDataReceived, AddressOf Async_Data_Received
AddHandler cmd.Exited, AddressOf CMD_Exited
cmd.Start()
pID1 = cmd.Id
cmd.BeginOutputReadLine()
cmd.BeginErrorReadLine()
Me.txtInputStringIn.Select() ' textbox where you can send more commands.
End Sub
'This event fires when process exited
Private Sub CMD_Exited(ByVal sender As Object, ByVal e As EventArgs)
'Me.Close()
MessageBox.Show("Process is exited.")
End Sub
'This part calls when Output received
Private Sub Async_Data_Received(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
Me.Invoke(New InvokeWithString(AddressOf Sync_Output), e.Data)
End Sub
Private Sub Sync_Output(ByVal text As String)
'an output textbox will show the output of the command prompt.
txtOutPut.AppendText(text & Environment.NewLine)
txtOutPut.ScrollToCaret()
End Sub