How to open a message box after a shell command - vb.net

I'm building an app for restarting the VNC service on remote computers.
I have multiple check boxes for multiple computers; I used the following command to do the work, and it works fine.
But I need to get a messagebox that shows either the command completed without errors, or if any error occurred, like access denied, shows that too.
If CheckBox2.CheckState = CheckState.Checked Then
Shell("psservice.exe \\192.168.1.48 -u .\user -p 123 restart WinVNC4", AppWinStyle.Hide)
End If
If CheckBox3.CheckState = CheckState.Checked Then
Shell("psservice.exe \\192.168.1.15 -u .\user -p 123 restart WinVNC4", AppWinStyle.Hide)
End If
Any help would be greatly appreciated!

Public Sub HandleService(strIP As String)
' Create the psservice.exe process object
Dim p As New Process()
' Set it to run hidden from user, so it appears smoother.
With p.StartInfo
.RedirectStandardOutput = True
.RedirectStandardError = True
.FileName = "psservice.exe"
.Arguments = String.Format("{0} {1} {2} {3}", "\\" & strIP, "-u .\user", "-p 123", "restart WinVNC4")
.UseShellExecute = False
.CreateNoWindow = True
End With
p.Start()
Dim myStreamReader As StreamReader = p.StandardError
' Read the standard error of psservice.exe and write it to console (or do your messagebox thing, etc.).
Console.WriteLine(myStreamReader.ReadLine())
' Wait for psservice.exe to finish before we handle it's output (Sync method, manding the thread won't continue until this one is finished. Use .exited if wanting to do Async)
' Also note that you can add milliseconds to this if wanted. i.e. .WaitForExit(1000)
p.WaitForExit()
End Sub
Call it like this:
HandleService("192.168.1.48")

Related

Check if Mage.exe batch manifest update was successful or not - ClickOnce

I have created a console app that creates a batch file in code, that will automatically update and re-sign my app manifest file using mage.exe when a new version gets published.
This batch file then gets executed by the same console app after it has created it.
I want to know if there is a way to determine if the mage.exe batch file failed in updating or signing the manifest?
Any help or ideas will be appreciated.
UPDATE
As per TnTinMn's comment, I forced the batch to fail on updating the manifest. This returned a exit code of 1. How is it then possible for me to extract that exit code to do my error handling? Im doing the following:
Dim procInfo As New ProcessStartInfo()
procInfo.UseShellExecute = True
procInfo.FileName = (sDriveLetter & ":\updatemanifest.bat")
procInfo.WorkingDirectory = ""
procInfo.Verb = "runas"
procInfo.WindowStyle = ProcessWindowStyle.Hidden
Dim sval As Object = Process.Start(procInfo) 'I tested the object to see if there is indeed a value that i can use.
While debugging and looking at the sval object's properties, the exit code is set to 1 but i can't seem to extract it from there.
There are two ways (that I know of) that you can wait for the process to exit before retrieving the Process.ExitCode.
The first as is a blocking call: Process.WaitForExit
and the second is to use the Exit event.
Private Sub RunProcess()
Dim psi As New ProcessStartInfo()
psi.UseShellExecute = True
psi.WindowStyle = ProcessWindowStyle.Hidden
psi.FileName = "cmd.exe"
psi.Arguments = "/c Exit 100"
Dim proc As Process = Process.Start(psi)
proc.EnableRaisingEvents = True
AddHandler proc.Exited, AddressOf ProcessExited
End Sub
Private Sub ProcessExited(sender As Object, e As EventArgs)
Dim proc As Process = DirectCast(sender, Process)
proc.Refresh()
Dim code As Int32 = proc.ExitCode
Me.BeginInvoke(Sub() MessageBox.Show(String.Format("Process has exited with code: {0}", code)), Nothing)
proc.Dispose()
End Sub

Execute DOS command within console application VB

I need to be able to execute a DOS command, such as 'ipconfig', using a command line application in Visual Basic. I can simply use start.process("CMD", "ipconfig"), but that opens a new instance of CMD. I want to be able to run a command like I would with CMD, using a console application, without opening another CMD window. Thanks!
You can use this to run the ipconfig command in hidden console window and redirect the output to local variable. From here you can manipulate it as needed:
Dim cmdProcess As New Process
With cmdProcess
.StartInfo = New ProcessStartInfo("cmd.exe", "/C ipconfig")
With .StartInfo
.CreateNoWindow = True
.UseShellExecute = False
.RedirectStandardOutput = True
End With
.Start()
.WaitForExit()
End With
' Read output to a string variable.
Dim ipconfigOutput As String = cmdProcess.StandardOutput.ReadToEnd

If, Then Statement to let User Know Command Could Not execute?

Lets just say we have this as Command1
Dim Command1 = "whoami.exe >> C:\Hello.Txt"
The program will read a list of users from a text file and then perform the action on each of them. If the user does not exist, or they are part of a password protected computer, I would like to see that in my printout.
I have this but am Unsure how to write the If Then Statement (If that is the ebst route to take)
For Each strUserName as String in strLines
Shell("cmd.exe /c" & Command1)
If Command1 = fail??
Then msgbox("Oops") ???
If you want to redirect the output of 'whoami.exe' to your own console, you can do the following:
Dim startInfo As New ProcessStartInfo()
startInfo.Arguments = "c:\Hello.txt"
startInfo.FileName = "c:\whoami.exe"
startInfo.RedirectStandardOutput = True
startInfo.UseShellExecute = False
Using process As Process = Process.Start(startInfo)
Using stream As StreamReader = process.StandardOutput
Console.Write(stream.ReadToEnd())
End Using
End Using
You will need to import the System.Diagnostics namespace. If 'whoami.exe' returns an exit code you can use, you can also use the Process class to check it by calling:
process.WaitForExit()
Dim code As Integer = process.ExitCode
If code = 1 Then
' success
Else
' other
End If
Hope this helps.
You need to write the If Then statement in either one line or multiple lines ending with an End If
If Command1 = fail Then msgbox("Oops")
or
If Command1 = fail Then
msgbox("Oops")
End If
Here is the msdn documentation for the if statement.

IO.File.Delete Random UnauthorizedAccessException

I'm using My.Computer.Filesystem.WriteAllBytes to write out an executable stored in my application's resources to it's startup directory. After running the executable, I then delete it. Everything works fine; however, I'll randomly get an UnauthorizedAccessException for no reason. After getting the exception, I can manually delete the file with no problem. Here's the full code:
' Convert MP3
' First, copy out converter
Dim Path = New IO.FileInfo(SoundPath)
Try
My.Computer.FileSystem.WriteAllBytes(Application.StartupPath + "\converter.exe", My.Resources.madplay, False)
Catch ex As Exception
MessageBox.Show(ex.ToString, "Report", MessageBoxButtons.OK)
Exit Sub
End Try
' Set up process
Dim MAD As New Process
' Set process info
Dim output As String = IO.Path.GetFileNameWithoutExtension(Path.FullName) + ".wav"
Dim input As String = Path.FullName
Dim adjust As String = barVolumeAdjust.Value.ToString
Dim hz As String = "15000"
With (MAD.StartInfo)
.FileName = Application.StartupPath + "\converter.exe"
.Arguments = "-v -a " + adjust + " -R " + hz + " -o """ + output + """ """ + input + """"
.UseShellExecute = False
.RedirectStandardInput = True
.RedirectStandardError = True
.RedirectStandardOutput = True
.CreateNoWindow = True
End With
' Start
MAD.Start()
' Update title with output
Dim Line As String = MAD.StandardError.ReadLine
While Not Line Is Nothing
Me.Text = Line
Line = MAD.StandardError.ReadLine
End While
' Stop
MAD.Close()
' Delete MAD
Try
IO.File.Delete(Application.StartupPath + "\converter.exe")
Catch ex As Exception
MessageBox.Show(ex.ToString, "Report", MessageBoxButtons.OK)
End Try
What perplexes me is that I literally just wrote out the executable, and nothing else could possibly be using it. I've checked the file attributes and it's not read-only. My application is also running as an administrator. What could be the problem?
You do not wait for the process to exit, so it is still running when you attempt to delete the file. See Process.WaitForExit
It looks like your using a separate process to write out the file - perhaps this is still using the file when you try to delete.
I suggest catching and handling the exception to get around the problem.

Pick up strings from cmd command? Process.StartInfo

so I've tried Process and starting a cmd.exe and send commands directly to that window. And then picking up the values written to the cmd.exe window.
The code looks like this:
Dim arrServers As ArrayList
Dim s(ListBoxServers.Items.Count) As String
ListBoxServers.Items.CopyTo(s, 0)
arrServers = New ArrayList(s)
Using P As New Process
P.StartInfo.FileName = "cmd.exe"
P.StartInfo.UseShellExecute = False
P.StartInfo.RedirectStandardOutput = True
P.StartInfo.RedirectStandardInput = True
P.Start()
For Each i In arrServers
P.StandardInput.WriteLine("query user " & txtBoxUsername.Text & " /server:" & i)
Next
P.StandardInput.WriteLine("exit")
Output = P.StandardOutput.ReadToEnd()
Trace.WriteLine(Output)
MsgBox(Output)
P.WaitForExit()
End Using
But is looks like it doesn't "press enter" or something. Meaning, I don't get any results from the command. I don't even get a "'command' is not recognized as an internal or external command, operable program or batch file." like you normally get if it doesn't understand the syntax.
Look into the Process class in the System.Diagnostics namespace for running your batch file.
Imagine the following really simple batch file called "hello.bat"
#ECHO OFF
echo Hello
You can call it and see "Hello" by using:
'Will hold the results of the batch
Dim Output As String
'Create a new process object
Using P As New Process()
'Set the script to run
P.StartInfo.FileName = "c:\scripts\hello.bat"
'My script doesn't take argument but this is where you would pass them
P.StartInfo.Arguments = ""
'Required to redirect output, don't both worrying what it means
P.StartInfo.UseShellExecute = False
'Tell the system that you want to see the output
P.StartInfo.RedirectStandardOutput = True
'Start your batch
P.Start()
'Read the entire contents of the outout
Output = P.StandardOutput.ReadToEnd()
'Wait until the batch is done running
P.WaitForExit()
End Using
'Do something with the output
Trace.WriteLine("Batch produced : " & Output)
Edit
Here's a version that doesn't run a batch but instead runs a couple of standard commands. We start by firing up a command shell to pass things to. One thing that sucks is that its hard to run a command, read the output and then run another command. The code below runs two commands back-to-back and dumps the entire result into a string. If you have a need for running a command, processing, running another command, I think you'll have to wire up something to StandardError and look at return codes. Before you do that, make sure you read up on problem with blocking and how other places solve it by wiring threads up such as here. Probably the easier way is to wrap this into a sub and call the sub once for each command.
'Will hold all of the text
Dim Output As String
'Create a new process object
Using P As New Process()
'Set the script to run the standard command shell
P.StartInfo.FileName = "cmd.exe"
'Required to redirect output, don't both worrying what it means
P.StartInfo.UseShellExecute = False
'Tell the system that you want to read/write to it
P.StartInfo.RedirectStandardOutput = True
P.StartInfo.RedirectStandardInput = True
'Start your batch
P.Start()
'Send your various commands
P.StandardInput.WriteLine("dir c:\")
P.StandardInput.WriteLine("ipconfig /all")
'Very important, send the "exit" command otherwise STDOUT will never close the stream
P.StandardInput.WriteLine("exit")
'Read the entire stream
Output = P.StandardOutput.ReadToEnd()
'Wait until the batch is done running
P.WaitForExit()
End Using
'Do something with the output
Trace.WriteLine(Output)
Edit 2
I'm having problems with the "query user" command in general, I can't get it to return anything for usernames with spaces in them even if I enclose the name in quotes. But here's a version that uses "quser" instead which does the exact same thing as far as I know.
'Will hold all of the text
Dim Output As String
'Create a new process object
Using P As New Process()
'Set the script to run the standard command shell
P.StartInfo.FileName = "cmd.exe"
'Required to redirect output, don't both worrying what it means
P.StartInfo.UseShellExecute = False
'Tell the system that you want to read/write to it
P.StartInfo.RedirectStandardOutput = True
P.StartInfo.RedirectStandardInput = True
'Start your batch
P.Start()
'Send your various commands
'Array of servers
Dim arrServers() As String = New String() {"SERVER1", "SERVER2"}
'Loop through array, wrap names with quotes in case they have spaces
For Each S In arrServers
P.StandardInput.WriteLine(String.Format("quser ""{0}"" /SERVER:{1}", Me.txtBoxUsername.Text, S))
Next
'Very important, send the "exit" command otherwise STDOUT will never close the stream
P.StandardInput.WriteLine("exit")
'Read the entire stream
Output = P.StandardOutput.ReadToEnd()
'Wait until the batch is done running
P.WaitForExit()
End Using
'Do something with the output
Trace.WriteLine(Output)
Use a library/class like NDesk's Options for flexible argument handling. If you don't want to use a external component, you'll have to loop over the arguments and process them manually:
For Each arg As String In Environment.GetCommandLineArgs()
Select Case arg
Case "/blah"
' process /blah '
Case "/foo"
' process foo '
Case Else
MsgBox "Unknown argument " + arg " found, aborting.", vbCritical
Environment.Exit(1)
End Select
Next
[I normally don't do VB, so this is just an untested sketch]