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
Related
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 trying to use command prompt as an admin using vb.net and I'm opening it by using runas in the default cmd.exe file. I want to then run commands through the newly opened command prompt window running as the domain admin using vb.net. How do I go about doing this?
This is the method that I'm using:
Public Sub runCmd(ByVal pass As String, ByVal command As String, ByVal arguments As String, ByVal 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
This is the call that opens cmd:
runCmd(strPass, "runas", "/user:<domain>\" + strUser + " cmd", False)
You need to set the Verb property of the StartInfo to "runas".
Public Sub runCmd(ByVal pass As String, ByVal command As String, ByVal arguments As String, ByVal 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"
pi.Verb = "runas"
p.StartInfo = pi
p.Start()
End Sub
And you then call your function like this:
runCmd(strPass, "", "/user:<domain>\" + strUser, False)
for 99% of all my Shell commands this code works fine, but for just one command I don't get the returned text, which is normally visible in the Shell window. Here is the code I use to execute commands and read back the results:
Function RunCommandCom(command As String, arguments As String) As String()
Dim p As Process = New Process()
Dim pi As ProcessStartInfo = New ProcessStartInfo()
Dim output As String
pi.Arguments = " " + "/c" + " " + command + " " + arguments
pi.FileName = "cmd.exe"
pi.RedirectStandardOutput = True 'pi.CreateNoWindow = True
pi.WindowStyle = ProcessWindowStyle.Normal
pi.UseShellExecute = False
p.StartInfo = pi
p.Start()
output = p.StandardOutput.ReadToEnd()
p.WaitForExit()
p.Close()
If output Is ""
Then
Return Nothing
Else
Return output.Replace(Chr(13), "").Split(Chr(10))
End If
End Function
The command making Problems is executed fine. It shall create a entry in a database, which defintly is existing after calling my funciton. Executing the command directly in a Shell generates the same entry and I can see the returned text.
Does anyone has an idea why the streamreader does not read/contain anything for exactly this one special command?
Arg. I found the problem. These two commands send their output to the StandardError stream. I don't know why, as no error occured.
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)
I am trying to run the shadow command in cmd.exe from my VB program but for some reason it will not run the command I have tried a few different things all of which have not worked. I was able to save the command to a batch file and then execute it with success but I would prefer to pass the argument/command to command prompt directly and execute this way. Basically, I have a the user run another program I created to extract sessionid and server name (I then just take the server number off the end). They get a 4 digit passcode on their end that is essentially first two is sessionid and last two are server number (all our servers are named ie smdts-(a number) so I just care about what server number they are on) I then take the four digit code and plug it into my shadow admin program. Here is my current code that doesn't work:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim SessId As String
Dim PassCode As String
Dim ServNum As String
Dim Prc As Process
PassCode = TextBox2.Text
SessId = PassCode.Substring(0, 2)
ServNum = PassCode.Substring(PassCode.Length - 2)
Dim fileargs As String = " shadow" + " " & SessId + " " + "/server:smdts-" + ServNum
Dim Pinfo = New System.Diagnostics.ProcessStartInfo
Pinfo.FileName = "cmd.exe"
Pinfo.Arguments = fileargs
Pinfo.ErrorDialog = False
Pinfo.UseShellExecute = False
Pinfo.CreateNoWindow = False
Pinfo.WindowStyle = ProcessWindowStyle.Normal
Pinfo.RedirectStandardOutput = False
Pinfo.RedirectStandardInput = True
Pinfo.RedirectStandardError = False
Prc = New Process
Prc.StartInfo = Pinfo
Prc.Start()
End Sub
What does work (batch file which I don't want to use):
Public Class Form1
Public pathvar As String = Environment.GetFolderPath(Environment.SpecialFolder.Personal)
Dim SessId As String
Dim PassCode As String
Dim ServNum As String
PassCode = TextBox2.Text
SessId = PassCode.Substring(0, 2)
ServNum = PassCode.Substring(PassCode.Length - 2)
Dim fileargs As String = " shadow" + " " & SessId + " " + "/server:smdts-" + ServNum
Dim Streamwriter As StreamWriter
Streamwriter = File.CreateText(pathvar + "\ShadowBatch.bat")
Streamwriter.WriteLine(fileargs)
Streamwriter.Close()
Shell(pathvar + "\ShadowBatch.bat")
End Sub
Any help on why the first example is not working would be GREATLY appreciated! Thanks!
Tom K
You're passing shadow ... as the arguments to CMD.
CMD does not support that.
Instead, you need to pass /c shadow ..., which will tell CMD to execute that command and exit.
Alternatively you could run shadow directly, without going through CMD.