Workaround mutually exclusive "UseShellExecute" and "RedirectStandardOutput" - vb.net

I need a workaround that will have the same effect as using True on both of the mutually exclusive UseShellExecute and RedirectStandardOutput.
The reason I need this is because I want to execute my perl script as I would through a CMD.exe:
perl perlcompare.pl <file1> <file2> <file3>
Noting that putting "perl" in front there seems to be optional as the file is already a .pl file.
I would like to have access to output the run gives me, as there might be error messages that are important to the user (e.g. missing files). Is there another way to achieve this?
Dim myProcess As New System.Diagnostics.Process
myProcess.StartInfo.WorkingDirectory = "K:\Engineering\Temp\perl"
myProcess.StartInfo.UseShellExecute = True
myProcess.StartInfo.FileName = "perlcompare.pl"
myProcess.StartInfo.Arguments = """" & MasterFile & """" & " " & """" & MasterOutput & """" & " " & """" & ComparisonsOutput & """"
myProcess.StartInfo.RedirectStandardOutput = True
myProcess.Start()
Dim sOutput As String
Using ProcessStreamReader As System.IO.StreamReader = myProcess.StandardOutput
sOutput = ProcessStreamReader.ReadToEnd()
End Using
MessageBox.Show(sOutput) 'txtOutput being the output textbox.
Quite new to the language and learning, so I will hound any clues you have for me.

I don't see why you want to set UseShellExecute to true. It needs to be false to allow redirection of the standard IO channels
If the location of the perl compiler executable is in PATH then you can just set FileName to perl. It's also better to use String.Format to build strings out of variables rather than use all those escaped quotes and concatenations
Dim myProcess As New System.Diagnostics.Process
myProcess.StartInfo.WorkingDirectory = "K:\Engineering\Temp\perl"
myProcess.StartInfo.UseShellExecute = False
myProcess.StartInfo.FileName = "perl"
myProcess.StartInfo.Arguments = String.Format(
"perlcompare.pl ""{0}"" ""{1}"" ""{2}""",
MasterFile,
MasterOutput,
ComparisonsOutput)
myProcess.StartInfo.RedirectStandardOutput = True
myProcess.Start()
If the location of the perl compiler isn't in the PATH then you can just put a fully-qualified file path into FileName

Related

Different results for exit conditions in process call

As a small part of a project, I'm calling ffmpeg to convert a video - I worked from an old example that used some of the output to create a progress bar, which was hanging. Code below:
[initiation code common to both examples]
Dim inputFile, outputFile As String, myProcess As New Process
Dim psiProcInfo As New ProcessStartInfo, ffreader As StreamReader
inputFile = "C:\Users\mklefass\Downloads\Video 2017-08-16 21.01.39.mov"
outputFile = "C:\Users\mklefass\Downloads\tmp2\Output"
psiProcInfo.FileName = Application.StartupPath + "\ffmpeg.exe" 'Location Of FFMPEG.EXE
psiProcInfo.Arguments = " -i " & Chr(34) & inputFile & Chr(34) & " -vf fps=5 " & Chr(34) & outputFile & "%d.jpg" & Chr(34) 'start ffmpeg with command strFFCMD string
psiProcInfo.UseShellExecute = False 'use the shell execute command we always want no
psiProcInfo.WindowStyle = ProcessWindowStyle.Hidden 'hide the ffmpeg process window
psiProcInfo.RedirectStandardError = True 'Redirect the error out so we can read it
psiProcInfo.RedirectStandardOutput = True 'Redirect the standard out so we can read it
psiProcInfo.CreateNoWindow = True
[bit that changes]
myProcess.Start()
ffreader = myProcess.StandardError
Do
Try
If Not myProcess.HasExited Then
'Debug.WriteLine(ffreader.ReadLine)
End If
Catch
If Not myProcess.HasExited Then
MsgBox("Something went wrong")
End If
End Try
Loop Until myProcess.HasExited
MsgBox("done")
I then found another example that just called the executable and then continued when it was done - as below:
[same initialisation]
Debug.WriteLine("Starting...")
myProcess.Start()
strOutput = myProcess.StandardError.ReadToEnd
myProcess.WaitForExit()
myProcess.Close()
Debug.Write(strOutput)
MsgBox("done")
The second approach worked perfectly... What's different about the "exit state" that Process.HasExited and Process.WaitForExit look for?

Batch start with parameters Error

I'm trying to make only a small program which includes the two steps decompile and start from a .java file. I can't run the .bat file correctly because DOS doesn't accept the spaces like I want.
Here's my code:
Process.Start("cmd.exe", "/c start "" """ & TextBoxJavacPath.Text & _
"""" & " " & """" & TextBoxFile.Text & """")
That's the string that comes out: (it's right)
/c "C:\Program Files\Java\jdk1.8.0_60\bin\javac.exe"
"C:\Users\Niklas\Desktop\Java\Kap06\src\eingabe\LetsReadLine.java"
If I enter it by typing in console it works, but via vb.net it doesn't work.
The error is the following: The command "C:/Program" is written incorrect or couldn't be found.
Try using this code (I don't know if it'll work) :
Private Sub Start(javacPath As String, file As String)
Using p As New Process With {
.StartInfo = New ProcessStartInfo With {
.WorkingDirectory = Path.GetDirectoryName(javacPath),
.Arguments = "/c """ & Path.GetFileName(javacPath) & """ """ & file,
.FileName = "cmd",
.CreateNoWindow = True}}
p.Start()
End Using
End Sub
And call the method like this :
Start(TextBoxJavacPath.Text, TextBoxFile.Text)
I can't test the code, because I don't have any .java file...

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)

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

vb.net How to pass a string with spaces to the command line

I am trying to call an external program using Process:
Dim strExe As String = "E:\Projects\Common Files\mktorrent.exe"
Dim p As New Process
Dim pinfo As New ProcessStartInfo
pinfo.UseShellExecute = False
pinfo.RedirectStandardOutput = True
pinfo.Arguments = " -a http://blah.com/announce.php -l " & FileSizeMarker & " " & fn
pinfo.FileName = strExe
pinfo.WorkingDirectory = fn.Substring(0, fn.LastIndexOf("\"))
pinfo.WindowStyle = ProcessWindowStyle.Normal
pinfo.CreateNoWindow = True
p.StartInfo = pinfo
p.Start()
The problem is with the filename (variable fn above). If it has spaces, the command chokes - without spaces, it works fine. I have tried adding 1, 2 or3 quotes, like this:
fn = Chr(34) & Chr(34) & Chr(34) & fn & Chr(34) & Chr(34) & Chr(34)
and also
fn = "\") & Chr(34) & fn & "\"& Chr(34)
and many other combinations, but it still gives me an error. Any thoughts on how I can get this to work?
TIA
It's really an old - but unsolved - problem.
My 2 cents of contribution.
Use CHR(34) before-and-after the string, delimiting it like:
Arg = "Name=" & chr(34) & "John Doe da Silva" & chr(34)
Just it!
Please check the below link, its in C#, may be its helpful to you
Word command-line-arguments space issues
Windows does not provide a common way of keeping arguments with spaces as single arguments. However there are a number of relatively common standards that you've tried.
So it comes down to either determining what argument processing mktorrent.exe uses or, as you're trying to pass a filename, using "MSDOS" 8.3 format for the path which will have no spaces.
For the latter, this answer points to the Win32API GetShortPathName.
Of course, 8.3 filenames can be disabled with modern Windows (all Windows NT-based systems I believe -- not that it often is). So your only full solution is to determine what argument processing mktorrent supplies.
Since your comment suggesting the quotes are not being passed through I confirmed I see 'testing' 'testing' '1 2 3' in the MsgBox output of this vbscript:
Option Explicit
Dim arg
Dim line
For Each arg in WScript.Arguments
line = line & " '" & arg & "'"
Next
MsgBox Trim(line)
when executed using:
Dim strExe As String = "C:\Windows\System32\wscript.exe"
Dim p As New Process
Dim pinfo As New ProcessStartInfo
pinfo.UseShellExecute = False
pinfo.RedirectStandardOutput = True
pinfo.Arguments = " G:\Utils\Arguments.vbs testing ""testing"" ""1 2 3"""
pinfo.FileName = strExe
pinfo.WorkingDirectory = "G:\Utils"
pinfo.WindowStyle = ProcessWindowStyle.Normal
pinfo.CreateNoWindow = True
p.StartInfo = pinfo
p.Start()
So wscript is seeing the quotes and is accumulating three arguments for the script.
BTW I just noticed your example attempts at getting quotes around the filename modify the fn variable. Did you cater for this with the .WorkingDirectory line, which should be using the unmodified filename?
This allows me to pass spaces to cmd. Hours of research turned up nothing; this thread came up constantly, hopefully this will help someone else.
Dim startprgm As New ProcessStartInfo("cmd.exe", "/C """"C:\Program Files (x86)\Folder\File""""" + strArguments)
note that the 4 double quotes lead the path, this part is important. leading the argument (/C) with 5 quotes doesn't work, but the trailing five can be divided into 4 and 1; and structured as such:
Dim startprgm As New ProcessStartInfo("cmd.exe", "/C """"C:\Program Files (x86)""""\Folder\File" + strArguments)
If you open cmd.exe and just send a command, you just need the first quote on the path (it doesn't need to be closed) but VB needs the trailing ones to "close" the quotes out.
best of luck, guys.
This WORKS:
Dim current_path, current_rulename, cmd1 as STRING
current_path = "C:\this folder\file name.exe"
current_rulename = "file name.exe"
cmd1 = "netsh advfirewall firewall add rule name = """ + current_rulename + """ dir = in action = block program = """ + current_path + """"
cmd1 &= " & "
cmd1 &= "netsh advfirewall firewall add rule name = """ + current_rulename + """ dir = out action = block program = """ + current_path + """"
cmd1 &= " & pause"
Process.Start("cmd", "/c " + cmd1)
Basically, the variables with spaces need to be enclosed like this:
""" + string_with_spaces + """
Broken into parts:
cmd1 =
"
netsh advfirewall firewall add rule name =
""" + current_rulename + """
dir=in action=block
program=
""" + current_path + """
"
This code joins two separate commands that use STRINGS with spaces.
Be simple:
Process.Start("c:\Your exe file", """" & "string with space" & """")