First of all, I did spend quite some time trying to find an answer to my issue but so far, nothing.
Here is the issue.
I have to run a cli command with arguments and redirect the output (REALTIME) to a textbox.
The command will look like this:
smcli - n name -f "script.scr"
Since smcli is not in the system PATH variable, I first need to go to the right Dir
So, I end up running the full command that looks like this:
cmd /c cd "c:\Program Files(x86)\bla\" && smcli - n name -f "script.scr"
I'll put the code in one sec but basically, the thing is working just fine, I get to the correct directory, run the command, it runs the script, all good.
I tried to redirect the output to a different form but then it doen't work anymore at all; anyway, that would be the bonus question.
The thing is though, when the script runs, it does say step by step what it is doing and sends that info to the output (i.e. restoring the name, restoring the disk config, ...)
As I said, I do get all those outputs....once EVERYTHING is completed (i.e. after 5 minutes of apparent freeze).
NOT redirecting the output to the textbox (i.e. leaving the console window open), I get the output real time.
So here is the code now (there is a bit of it, sorry):
Private Sub ReadMDcfgToolStripButton_Click(sender As Object, e As EventArgs) Handles ReadMDcfgToolStripButton.Click
Dim exitcode As Int32
smcli = Environment.GetEnvironmentVariable("ProgramFiles") & "\Dell\MD Storage Manager\client\" 'SMcli.exe"
Dim gotosmclidir As String = "cd """ & smcli & """"
Dim smclicommand As String
smclicommand = "smcli -n " & mdname & " -c ""save storagearray configuration file=\""" & saveFileDialog2.FileName & "\"" allconfig"";"
cmd = gotosmclidir & " && " & smclicommand
exitcode = RunCommandCom(cmd, "", False)
End Sub
Private Function RunCommandCom(command As String, arguments As String, permanent As Boolean) As Int32
' Usage:
'RunCommandCom("DIR", "/W", true)
'For the multiple command on one line the key are the & | && and || command connectors
'•A & B -> execute command A, then execute command B.
'•A | B -> execute command A, and redirect all it's output into the input of command B.
'•A && B -> execute command A, evaluate the errorlevel after running Command A, and if the exit code (errorlevel) is 0, only then execute command B.
'•A || B -> execute Command A, evalutate the exit code of this command and if it's anything but 0, only then execute command B.
TextBox1.Text = "Communication in progress..." & vbCrLf
exitcodelbl.Text = ""
CmdCloseBtn.Enabled = False
ToolStrip1.Enabled = False
CmdOutputPanel.Visible = True
Dim p As Process = New Process()
Dim pi As ProcessStartInfo = New ProcessStartInfo()
pi.FileName = "cmd.exe"
pi.Arguments = " " + If(permanent = True, "/K", "/C") + " " + command + " " + arguments
MsgBox(pi.Arguments)
pi.CreateNoWindow = True
pi.UseShellExecute = False
pi.RedirectStandardOutput = True
pi.RedirectStandardError = True
pi.CreateNoWindow = True
p.StartInfo = pi
AddHandler p.OutputDataReceived, AddressOf GotData
p.Start()
p.BeginOutputReadLine()
Do Until p.HasExited
Loop
exitcodelbl.Text = p.ExitCode
Select Case p.ExitCode
Case 0
exitcodelbl.Text += " - Command completed successfully."
Case 1
exitcodelbl.Text += " - Could not communicate with the Array."
Case 9
exitcodelbl.Text += " - Could not communicate with the Array."
Case 14
exitcodelbl.Text += " - Could not communicate with the Array."
Case Else
exitcodelbl.Text += " - Unknown code."
End Select
Return p.ExitCode
End Function
Sub GotData(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
UpdateTextBox(e.Data)
End Sub
Private Delegate Sub UpdateTextBoxDelegate(ByVal Text As String)
Private Sub UpdateTextBox(ByVal Tex As String)
If Me.InvokeRequired Then
Dim del As New UpdateTextBoxDelegate(AddressOf UpdateTextBox)
Dim args As Object() = {Tex}
Me.Invoke(del, args)
Else
TextBox1.Text &= Tex & Environment.NewLine
End If
End Sub
Apart from the command itself, everything else is comming from researches and they are just put together, the way to run the command on one hand and the redirection on the other hand.
I believe the issue is with my command (looks like that part is VERY sensible).
Either I can have it done a different way (again, it works but not real time) or I missed something trivial...?
Thanks for your help.
Following tinstaafl here is the updated code:
Private Sub MainForm_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
copyrightlbl.Focus()
wait(1000)
Await RunCommandCom("", "192.168.219.152", False) '172.16.1.55
CmdCloseBtn.Enabled = True
End Sub
Private Async Function RunCommandCom(command As String, arguments As String, permanent As Boolean) As Task(Of Int32)
TextBox1.Text = "Communication in progress..." & vbCrLf
exitcodelbl.Text = ""
CmdCloseBtn.Enabled = False
ToolStrip1.Enabled = False
CmdOutputPanel.Visible = True
Dim p As Process = New Process()
With p.StartInfo
.WorkingDirectory = Environment.GetEnvironmentVariable("ProgramFiles") & "\Dell\MD Storage Manager\client\" 'Directory for SMcli.exe"
.FileName = "ping.exe"
.Arguments = command + " " + arguments
.CreateNoWindow = True
.UseShellExecute = False
.RedirectStandardOutput = True
.RedirectStandardError = True
End With
p.Start()
Do Until p.HasExited
TextBox1.Text &= Await p.StandardOutput.ReadLineAsync
Loop
exitcodelbl.Text = p.ExitCode
Return p.ExitCode
End Function
Instead of using a delegate try reading the output directly to the textbox. The StandardOutput property of the Process is a stream that you can use the ReadLineAsync method to get the output. Something like this should work:
pi.CreateNoWindow = True
pi.UseShellExecute = False
pi.RedirectStandardOutput = True
pi.RedirectStandardError = True
p.StartInfo = pi
p.Start()
Do Until p.HasExited
TextBox1.Text &= Await p.StandardOutput.ReadLineAsync
Loop
Intellisense will prompt you to make the sub routine Async, but with that you should get the output you want.
Private Async Function RunCommandCom(command As String, arguments As String, permanent As Boolean) As Task(Of Int32)
Await RunCommandCom("", "192.168.219.152", False)
Related
Ive created a screen recorder and stream recorder that can run at the same time. I'm using FFMPEG to perform the recordings which are triggered using the Process.Start() function. Now if both recording options are running, I have 2 FFMPEG programs running and 2 Console windows running. So, I need to be able to close or exit either recording options independently without it effecting the other.
So far this is my code...
Dim cmdstr = "/k ffmpeg.exe -y -probesize 10M -rtbufsize 1500M -f dshow -i audio=""" &
Audio_name & """ -acodec pcm_s16le -f gdigrab -framerate 60 -i desktop -vcodec libx264 -qp 0
-threads 0 -crf 18 -preset ultrafast -tune zerolatency " & str & "\Recordings\ScreenRecorder"
& FileTime & ".mkv"
If TestFF = True Then
MsgBox("1. Test Mode: " & cmdstr)
Process.Start("cmd.exe", "/k ffmpeg.exe -list_devices true -f dshow -i dummy") ' This lists all devices
Process.Start("cmd.exe", cmdstr)
RTBStreamData1.Text = cmdstr
RecordingInProcess = True
RecordingOn = True
Else
Dim startInfo As New ProcessStartInfo("cmd.exe")
startInfo.Arguments = cmdstr
startInfo.WindowStyle = ProcessWindowStyle.Hidden
startInfo.CreateNoWindow = True
startInfo.UseShellExecute = False
Process.Start(startInfo)
RecordingInProcess = True
RecordingOn = True
Dim ProcID As Integer = Process.GetCurrentProcess.Id
Label13.Text = ProcID.ToString
I am able to get the ID of the application itself, but it is the individual processes of ffmpeg and the associated console window that I need to be able to close at the same time.
any form of help would be really appreciated. Thank you.
In the image above you can see that i was able to track the first process, but not the second!!!
If I well understood: A trick you can use might be you pass a “FAKE” custom argument to each process you’re going to start by Process.Start(). After that looping running processes you can recognize your target process.
The code below gives an idea. (There is to change something in the SQL String in order to avoid SQL injection)
Dim PROCESS_CUSTOM_ARG As String = "UNIQUE_STRING_ID_HERE" '& _1 also add an integer incremental for each process
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim startInfo As New ProcessStartInfo("cmd.exe")
startInfo.Arguments = " - a lot of your Arguments here"
startInfo.Arguments &= " - " & PROCESS_CUSTOM_ARG
startInfo.WindowStyle = ProcessWindowStyle.Hidden
startInfo.CreateNoWindow = True
startInfo.UseShellExecute = False
Process.Start(startInfo)
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim searcher As New System.Management.ManagementObjectSearcher("root\CIMV2", "SELECT ProcessId, CommandLine FROM Win32_Process WHERE CommandLine LIKE '%" & PROCESS_CUSTOM_ARG & "' ")
For Each p As System.Management.ManagementObject In searcher.Get()
Dim ProcessId As String = p("ProcessId")
Dim CommandLine As String = p("CommandLine")
Console.WriteLine($"CommandLine: {CommandLine} ProcessId: {ProcessId}")
Dim Proc As Process = Process.GetProcessById(ProcessId)
Proc.Kill()
Next
End Sub
I am looking for the answer to my question, here I clearly explain the situation to you.
I have a richtextbox, and 10 lines included.
line1
line2
line3
line4
line5
..
line10
and i am doing this;
For i = 0 to richtextbox.Lines.Count = - 1
Button1_Click;
Dim getlist as Process = Process.Start("cmd", "commands" + richtextbox.Lines(i))
getlist.WaitForExit()
Next
I tried this but it didn't work;
Dim getlist as Process = Process.Start("cmd", "commands" + richtextbox.Lines(i))
Dim getlist2 as Process = Process.Start("cmd", "commands" + richtextbox.Lines(i + 1))
when I do this, it gets like;
first: richtextbox line 0 and line 1
and getlist starts line1 and getlist2 starts line2
I want it to be like;
getlist: line0
getlist2: line1
and who finishes earlier, the function starts to get new line;
for example:
getlist: line0-line2-line3-line5
getlist2: line1-line4-line6
how could I do this? thanks a lot for all answers!
In order to do this, you'll need to use some sort of threading-based solution to execute in parallel. Probably the most straightforward of these is to use Task-based asynchronous methods.
This would end up looking something like this:
Dim T1 = Task.Run(Sub()
Dim getlist as Process = Process.Start("cmd", "commands" & richtextbox.Lines(1))
getlist.WaitForExit()
End Sub)
Dim T2 = Task.Run(Sub()
Dim getlist as Process = Process.Start("cmd", "commands" & richtextbox.Lines(2))
getlist.WaitForExit()
End Sub)
Then, you would be able to use various approaches to wait for one or both tasks to finish. If you mark the containing routine as Async you could Await either T1 or T2. You can also Await Task.WhenAny to continue as soon as either one finishes.
I would strongly recommend reading the MSDN documentation on Task-based asynchrony. In particular, you should read "Implementing the Task-based Asynchronous Pattern", as the sections on Interleaving and Throttling are likely to be applicable to what you want to do. Unfortunately, the examples are all in C# in my copy of the online help, but the material should be straightforward to translate to VB.
If I understand your intention, this is the perfect application for threading. This works - I just wrote it, compiled it and ran it. To see it do some simple tasks in separate threads, running just 2 threads at a time until all commands have been executed, just enter valid shell commands into your richtextbox1. To test it, I used:
Echo A
Echo B
Echo C
Echo D
NOTES:
I appended a PAUSE into the CMD arguments, so you can see clearly what's happening.
Threading has a lot of nuances that I can't begin to get into here such as ApartmentState, but it's something that you'll want to educate yourself about.
I'm no guru, and I'm sure there are some more elegant ways to handle some of this 😄
Hope this helps!
Imports System.Threading
Public Class Form1
Dim Thread1 As Thread
Dim Thread2 As Thread
Dim Thread1cmd As String = ""
Dim Thread2cmd As String = ""
Dim Thread1Running As Boolean = False
Dim Thread2Running As Boolean = False
Dim LastCmd As Int32 = -1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
LastCmd = -1
While LastCmd < RichTextBox1.Lines.Count
If Thread1Running = False And (LastCmd + 1) < RichTextBox1.Lines.Count Then
LastCmd += 1
Thread1cmd = RichTextBox1.Lines(LastCmd)
output.Text += LastCmd.ToString + " Thread 1: " + Thread1cmd + vbCrLf
Thread1 = New Thread(AddressOf Thread1Helper)
Thread1.IsBackground = True
Thread1.Start()
End If
If Thread2Running = False And (LastCmd + 1) < RichTextBox1.Lines.Count Then
LastCmd += 1
Thread2cmd = RichTextBox1.Lines(LastCmd)
output.Text += LastCmd.ToString + " Thread 2: " + Thread2cmd + vbCrLf
Thread2 = New Thread(AddressOf Thread2Helper)
Thread2.IsBackground = True
Thread2.Start()
End If
'Note - there is debate around the DoEvents that follows, so experiment with it and see what works best for you
Application.DoEvents()
'the main thread now sleeps to give helper threads time to do some work:
System.Threading.Thread.Sleep(200)
End While
End Sub
Sub Thread1Helper()
Thread1Running = True
Dim p1 As System.Diagnostics.Process
Try
p1 = New System.Diagnostics.Process
Dim MyCMD As String = Environment.GetFolderPath(Environment.SpecialFolder.System) + "\cmd"
p1.StartInfo.FileName = MyCMD
p1.StartInfo.Arguments = " /c " + """" + Thread1cmd + "&&PAUSE" + """"
'MsgBox(p1.StartInfo.FileName.ToString)
p1.Start()
p1.WaitForExit()
p1.Close()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
Thread1Running = False
End Sub
Sub Thread2Helper()
Thread2Running = True
Dim p2 As System.Diagnostics.Process
Try
p2 = New System.Diagnostics.Process
Dim MyCMD As String = Environment.GetFolderPath(Environment.SpecialFolder.System) + "\cmd"
p2.StartInfo.FileName = MyCMD
p2.StartInfo.Arguments = " /c " + """" + Thread2cmd + "&&PAUSE" + """"
'MsgBox(p2.StartInfo.FileName.ToString)
p2.Start()
p2.WaitForExit()
p2.Close()
Catch ex As Exception
MsgBox(ex.ToString)
End Try
Thread2Running = False
End Sub
End Class
I m developing a windows application that will use pdf2text pilot software which supports command line. In this application user needs to specify location of the pdf file. I m able to open cmd but could not pass commands to it or somehow my commands are not getting executed.
Imports System
Imports System.IO
Imports System.Diagnostics.Process
Imports System.Diagnostics.ProcessStartInfo
Public Class EDCS
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim dlgrslt As DialogResult = OpenFileDialog1.ShowDialog()
Dim fnames As String() = OpenFileDialog1.FileNames
Dim txtfnames As String
For i = 0 To fnames.Length - 1
If TextBox1.Text = "" Then
TextBox1.Text = fnames(i)
Else
TextBox1.Text = TextBox1.Text + " / " + fnames(i)
End If
txtfnames = fnames(i).Replace(".pdf", ".txt")
File.Create(txtfnames).Dispose()
Dim convertcommand As String = "textextract """ & fnames(i) & """ /to """ & txtfnames & """"
'/c will exit cmd and /k will keep it open
'Shell("cmd.exe /c textextract "C:\Users\user\Desktop\ Part 1.pdf" /to " C:\Users\user\Desktop\ Part 1.txt"")
'SendKeys.Send(convertcommand)
'SendKeys.Send("{ENTER}")
Dim p As New Process
p.StartInfo.FileName = "cmd.exe"
'p.StartInfo.WorkingDirectory = "C:\Program Files (x86)\Two Pilots\PDF2Text Pilot"
'p.StartInfo.Arguments = "textextract "C:\Users\user\Desktop\ Part 1.pdf" /to " C:\Users\user\Desktop\ Part 1.txt""
p.StartInfo.UseShellExecute() = False
p.StartInfo.RedirectStandardInput = True
p.StartInfo.RedirectStandardOutput = True
p.Start()
p.StandardInput.WriteLine(convertcommand)
'Dim process As New Process()
'process.StartInfo.FileName = "cmd.exe "
'process.StartInfo.Verb = "runas"
'process.StartInfo.UseShellExecute = False
'process.StartInfo.RedirectStandardInput = True
'process.StartInfo.RedirectStandardOutput = True
'process.Start()
'process.StandardInput.WriteLine("textextract "C:\Users\user\Desktop\ Part 1.pdf" /to " C:\Users\user\Desktop\ Part 1.txt"")
'process.StandardInput.WriteLine("exit")
'process.Close()
Next
End Sub
End Class
OS: Windows 7
vb.net developer
Thanks in Advance
OK, here you go, this should work, turns out that pdf2text has a bug in it, but I managed to get it working by passing a couple of blank lines before the command runs and then also adding a wait for the utility to complete before the command prompt closes. You may need to increase that wait for large files I guess. I get some "The handle is invalid" messages in the command prompt and I suspect they are from pdf2text too, but it looks like its safe to ignore them.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim dlgrslt As DialogResult = OpenFileDialog1.ShowDialog()
Dim fnames As String() = OpenFileDialog1.FileNames
Dim txtfnames As String
For i = 0 To fnames.Length - 1
If TextBox1.Text = "" Then
TextBox1.Text = fnames(i)
Else
TextBox1.Text = TextBox1.Text + " / " + fnames(i)
End If
txtfnames = fnames(i).Replace(".pdf", ".txt")
File.Create(txtfnames).Dispose()
Dim convertcommand As String = "textextract.exe """ & fnames(i) & """ /to """ & txtfnames & """"
Dim p As New Process
p.StartInfo.FileName = "cmd.exe"
p.StartInfo.Arguments = " /k"
p.StartInfo.UseShellExecute = False
p.StartInfo.RedirectStandardInput = True
p.StartInfo.RedirectStandardOutput = False
p.Start()
Using sw As StreamWriter = p.StandardInput
If sw.BaseStream.CanWrite Then
sw.WriteLine()
sw.WriteLine()
sw.WriteLine(convertcommand)
p.WaitForExit(3000)
End If
End Using
Next
End Sub
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
The question is self-explanatory. It would be great if the code was one line long (something to do with "Process.Start("...")"?). I researched the web but only found old examples and such ones that do not work (at least for me). I want to use this in my class library, to run Git commands (if that helps?).
You could try this method:
Public Class MyUtilities
Shared Sub RunCommandCom(command as String, arguments as String, permanent as Boolean)
Dim p as Process = new Process()
Dim pi as ProcessStartInfo = new ProcessStartInfo()
pi.Arguments = " " + if(permanent = true, "/K" , "/C") + " " + command + " " + arguments
pi.FileName = "cmd.exe"
p.StartInfo = pi
p.Start()
End Sub
End Class
call, for example, in this way:
MyUtilities.RunCommandCom("DIR", "/W", true)
EDIT: For the multiple command on one line the key are the & | && and || command connectors
A & B → execute command A, then execute command B.
A | B → execute command A, and redirect all it's output into the
input of command B.
A && B → execute command A, evaluate the errorlevel after running
Command A, and if the exit code (errorlevel) is 0, only then execute
command B.
A || B → execute Command A, evaluate the exit code of this command
and if it's anything but 0, only then execute command B.
You Can try This To Run Command Then cmd Exits
Process.Start("cmd", "/c YourCode")
You Can try This To Run The Command And Let cmd Wait For More Commands
Process.Start("cmd", "/k YourCode")
I was inspired by Steve's answer but thought I'd add a bit of flare to it.
I like to do the work up front of writing extension methods so later I have less work to do calling the method.
For example with the modified version of Steve's answer below, instead of making this call...
MyUtilities.RunCommandCom("DIR", "/W", true)
I can actually just type out the command and call it from my strings like this...
Directly in code.
Call "CD %APPDATA% & TREE".RunCMD()
OR
From a variable.
Dim MyCommand = "CD %APPDATA% & TREE"
MyCommand.RunCMD()
OR
From a textbox.
textbox.text.RunCMD(WaitForProcessComplete:=True)
Extension methods will need to be placed in a Public Module and carry the <Extension> attribute over the sub. You will also want to add Imports System.Runtime.CompilerServices to the top of your code file.
There's plenty of info on SO about Extension Methods if you need further help.
Extension Method
Public Module Extensions
''' <summary>
''' Extension method to run string as CMD command.
''' </summary>
''' <param name="command">[String] Command to run.</param>
''' <param name="ShowWindow">[Boolean](Default:False) Option to show CMD window.</param>
''' <param name="WaitForProcessComplete">[Boolean](Default:False) Option to wait for CMD process to complete before exiting sub.</param>
''' <param name="permanent">[Boolean](Default:False) Option to keep window visible after command has finished. Ignored if ShowWindow is False.</param>
<Extension>
Public Sub RunCMD(command As String, Optional ShowWindow As Boolean = False, Optional WaitForProcessComplete As Boolean = False, Optional permanent As Boolean = False)
Dim p As Process = New Process()
Dim pi As ProcessStartInfo = New ProcessStartInfo()
pi.Arguments = " " + If(ShowWindow AndAlso permanent, "/K", "/C") + " " + command
pi.FileName = "cmd.exe"
pi.CreateNoWindow = Not ShowWindow
If ShowWindow Then
pi.WindowStyle = ProcessWindowStyle.Normal
Else
pi.WindowStyle = ProcessWindowStyle.Hidden
End If
p.StartInfo = pi
p.Start()
If WaitForProcessComplete Then Do Until p.HasExited : Loop
End Sub
End Module
Sub systemcmd(ByVal cmd As String)
Shell("cmd /c """ & cmd & """", AppWinStyle.MinimizedFocus, True)
End Sub
Imports System.IO
Public Class Form1
Public line, counter As String
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
counter += 1
If TextBox1.Text = "" Then
MsgBox("Enter a DNS address to ping")
Else
'line = ":start" + vbNewLine
'line += "ping " + TextBox1.Text
'MsgBox(line)
Dim StreamToWrite As StreamWriter
StreamToWrite = New StreamWriter("C:\Desktop\Ping" + counter + ".bat")
StreamToWrite.Write(":start" + vbNewLine + _
"Ping -t " + TextBox1.Text)
StreamToWrite.Close()
Dim p As New System.Diagnostics.Process()
p.StartInfo.FileName = "C:\Desktop\Ping" + counter + ".bat"
p.Start()
End If
End Sub
End Class
This works as well