How to check when a powershell script is done running with Visual Studio - vb.net

I am running a powershell script from within my app. I can start it and it completes without problem, but how do I check when its done? I need to do other actions, Only once its complete. Here is what I have, for a bit of reference:
Public Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
If requiredEnd = True And requiredPass = True And requiredPath = True And requiredSheet = True And requiredStart = True And requiredUser = True Then
For I = 0 To 7
objWriter.WriteLine(aryText(I))
Next
objWriter.Close()
Dim p As Process
p.Start("powershell", "-ExecutionPolicy ByPass -windowstyle hidden -file .\\Excel.ps1")
p.WaitForExit()
If p.HasExited = True Then
MsgBox("The Process Has Been Completed!")
Application.Exit()
End If
Else
If requiredEnd = False Or requiredPass = False Or requiredSheet = False Or requiredStart = False Or requiredUser = False Then
MessageBox.Show("You Have Missing Required Fields!")
Else
MessageBox.Show("That Is Not A Valid File!")
End If
End If
End Sub
Thanks for the help! Also, I am a beginner at this, so could you do a bit of an explanation on how it works if it isn't super straight forward? Thanks again.
Edit: I realize I forgot the key point: I can close the script, but I need to know if it exited successfully. So a way to check errors, basically.

Why not use a runspace to execute your powershell code in-process, instead of shelling out to an external application? Most of the code you see for this is in C# but you can of course do it in VB.net as well:
' create Powershell runspace
Dim MyRunSpace As Runspace = RunspaceFactory.CreateRunspace()
' open it
MyRunSpace.Open()
' create a pipeline and feed it the script text
Dim MyPipeline As Pipeline = MyRunSpace.CreatePipeline()
MyPipeline.Commands.AddScript(scriptText)
' add an extra command to transform the script output objects into nicely formatted strings
' remove this line to get the actual objects that the script returns. For example, the script
' "Get-Process" returns a collection of System.Diagnostics.Process instances.
MyPipeline.Commands.Add("Out-String")
' execute the script
Dim results As Collection(Of PSObject) = MyPipeline.Invoke()
' close the runspace
MyRunSpace.Close()
This code is taken from this MSDN blog which has a much more thorough and complete example application, but the basic idea here is to run the code natively in your application and then you can get the output any way you like to test for errors.

Related

I cannot figure out ProcessStartInfo and to be able to use Process.Start.StandardOutput.ReadToEnd to display what is happening in Command line

I am trying to display what is going on in Command Line Interface without opening up command line. I know Process class has that ability but I am having a hard time with StandardOutput.ReadOnly. I have the process set to one button and then after I click, I want it to show a status that command line is in fact connecting. Thoughts?
Private Sub EstablishConnection_Click(sender As Object, e As EventArgs) Handles EstablishConnection.Click
' One file parameter to the executable
Dim sourceName1 As String = strXFileName
Dim sourceName2 As String = strYFileName
' New ProcessStartInfo created
Dim p As New ProcessStartInfo
' Specify the location of the binary
p.FileName = "C:\Software\John\Doe"
' Use these arguments for the process
p.Arguments = $"-Application -Connect -Example -E"" {stXFileName} "" -S"" {strYFileName} """
' Use a hidden window
p.WindowStyle = ProcessWindowStyle.Hidden
p.UseShellExecute = False
p.RedirectStandardOutput = True
p.RedirectStandardError = True
p.RedirectStandardInput = True
' Start the process
Process.Start(p)
'Dim output As String = p.StandardOutputEncoding
'Open the Status Screen form once connection is established
StatusScreen.Show()
Me.Hide()
End Sub

run external program and exit from calling program

n the closing procedure of a program (just before running Application.exit) I need to run an external program to pass a parameter and exit the calling program.
The program called (FilmDB_Update.exe) has the task of overwriting the main program or a dll library.
I tried to use the "process.start" technique, but apparently, the calling program remains in use and does not allow me to overwrite it.
This is the code that I write:
Private Sub AggiornaPgm()
Dim ws_file As String = "FilmDB_Update.exe"
Dim ws_proc_param As String = """" + ws_working_path + """ " + ws_temp_path
Dim ws_fullPath As String = Path.Combine(ws_temp_path, ws_file)
If File.Exists(ws_fullPath) Then
File.Copy(ws_fullPath, ws_file, True)
End If
Dim proc As New System.Diagnostics.Process()
proc = Process.Start(ws_file, ws_proc_param)
End Sub
I wanted to try using the shell command, but I can not pass the parameters to the called program.
Does any of you have any other ideas about it?
Thank you
Marcello
As Ahmed suggested, I added the test for the calling process to the program called.
p = Process.GetProcessesByName(ws_calling_pgm)
While p.Count > 0
Threading.Thread.Sleep(3000)
p = Process.GetProcessesByName(ws_calling_pgm)
End While
p = Nothing
When I exit the While loop, the calling process is terminated. I do not understand why, despite the process no longer exists, the main program is still in use.

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

How to get Output of a Command Prompt Window line by line in Visual Basic?

I am trying to get a command line output line by line till the end of the output but I am not able to do so. I am using it in my Form and this code executes on click of a button.
Can you tell me whats wrong with my code?
Dim proc As ProcessStartInfo = New ProcessStartInfo("cmd.exe")
Dim pr As Process
proc.CreateNoWindow = True
proc.UseShellExecute = False
proc.RedirectStandardInput = True
proc.RedirectStandardOutput = True
pr = Process.Start(proc)
pr.StandardInput.WriteLine("cd C:\sdk\platform-tools\")
pr.StandardInput.WriteLine("adb help")
Dim helpArray(20) as String
For i as Integer 1 To 7
helpArray(i) = pr.StandardOutput.ReadLine()
Next
pr.StandardOutput.Close()
The program stops responding when this code is executed.
I've done some research. adb help writes output into STDERR. So you need something like:
Dim proc As ProcessStartInfo = New ProcessStartInfo("cmd.exe")
Dim pr As Process
proc.CreateNoWindow = True
proc.UseShellExecute = False
proc.RedirectStandardInput = True
proc.RedirectStandardOutput = True
pr = Process.Start(proc)
pr.StandardInput.WriteLine("C:\sdk\platform-tools")
pr.StandardInput.WriteLine("adb help 2>&1")
pr.StandardInput.Close()
Console.WriteLine(pr.StandardOutput.ReadToEnd())
pr.StandardOutput.Close()
to catch it.
You need no 2>&1 if you call ipconfig, for example.
Do not interate over the output and do not read it! Normally you don't know how long the output (same goes for error output too) would be, so you need to prepare for an unknown length. Since you are telling the Process class, that you want to handle the standard output and the standard error by yourself, you also need to bind to the events, in this case:
OutputDataReceived
ErrorDataReceived
or to block the current process and read the complete output at once like #Dmitry Kurilo does in his answer. I find the first approach better because I do not need to wait for the process to end to see it's output. The MSDN documentation of the ProcessStartInfo.RedirectstandardError property gives a good explanation of the different possibilities with a lot of examples.
If you want to take a specific line, there are a lot of possibilities. One would be to store each output (line) in the delegate and use it later, using a List(Of String) and output the specific line when the process is done (= all output lines are present).
A possible solution could look like this:
' store error output lines
dim lines = new List(of String)
dim executable = "c:\temp\android\sdk\platform-tools\adb.exe"
dim arguments = " help"
dim process = new Process()
process.StartInfo = createStartInfo(executable, arguments)
process.EnableRaisingEvents = true
addhandler process.Exited, Sub (ByVal sender As Object, ByVal e As System.EventArgs)
Console.WriteLine(process.ExitTime)
Console.WriteLine(". Processing done.")
' output line n when output is ready (= all lines are present)
Console.WriteLine(lines(4))
end sub
' catch standard output
addhandler process.OutputDataReceived, Sub (ByVal sender As Object, ByVal e As System.Diagnostics.DataReceivedEventArgs)
if (not String.IsNullOrEmpty(e.Data))
Console.WriteLine(String.Format("{0}> {1}", DateTime.Now.ToString("dd.MM.yyyy HH:mm:ss") ,e.Data))
end if
end sub
' catch errors
addhandler process.ErrorDataReceived, Sub (ByVal sender As Object, ByVal e As System.Diagnostics.DataReceivedEventArgs)
'Console.WriteLine(String.Format("! {0}", e.Data))
' add every output line to the list of strings
lines.Add(e.Data)
end sub
' start process
dim result = process.Start()
' and wait for output
process.BeginOutputReadLine()
' and wait for errors :-)
process.BeginErrorReadLine()
private function createStartInfo(byval executable as String, byval arguments as String) as ProcessStartInfo
dim processStartInfo = new ProcessStartInfo(executable, arguments)
processStartInfo.WorkingDirectory = Path.GetDirectoryName(executable)
' we want to read standard output
processStartInfo.RedirectStandardOutput = true
' we want to read the standard error
processStartInfo.RedirectStandardError = true
processStartInfo.UseShellExecute = false
processStartInfo.ErrorDialog = false
processStartInfo.CreateNoWindow = true
return processStartInfo
end function
Now even if the adb writes to the error output, you will be able to see it. It will also be complete.
The output in this case looks like this:
14.10.2014 12:49:10
. Processing done.
-e - directs command to the only running emulator.
Another possibility would be to put everything into one string and after the process has finished split the single string on line endings (CRLF \r\n) and you will gain the lines you want to filter.

Process.Start not passing along Arguments

I have a bat file that runs the following perfectly fine:
Bec.exe --f=Config.cfg
Now in vb.net I have a button that starts the same exe with the same arguments, and outputs to a rtb. However it does not pass along the arguments for some reason, I don't know why. Can anyone help?
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
Dim cmdProcess As Process
cmdProcess = New Process()
cmdProcess.StartInfo.FileName = """" & TextBox2.Text & """" 'normally this is C:\ServerTools\Bec.exe
cmdProcess.StartInfo.Arguments = """" & TextBox1.Text & """" 'normally is --f=Config.cfg
cmdProcess.StartInfo.RedirectStandardOutput = True
cmdProcess.StartInfo.UseShellExecute = False
If cmdProcess.Start() Then
RichTextBox2.Text = cmdProcess.StandardOutput.ReadToEnd
Else
' Failed to execute
End If
End Sub
Also I'll provide a reference of the accepted options to the .exe I'm starting
Options:
-h, --help show this help message and exit
-f FILENAME, --file=FILENAME
Try to use the ProcessStartInfo.WorkingDirectory property.
I've always done this by creating a separate ProcessStartInfo object and passing that into the Process.Start() method.
ProcessStartInfo psi = new ProcessStartInfo("filename.txt", "-arg1 -arg2");
Process.Start(psi);
You should not quote the arguments, nor the exe path
cmdProcess.StartInfo.FileName = TextBox2.Text
cmdProcess.StartInfo.Arguments = TextBox1.Text
I've come across a solution, apparently I had to be running my program in the same directory as the exe I was executing. the -f Config.cfg argument is normally based off the location of where Bec.exe is, well when I was calling it through my program it was basing it off of my programs location, so now that I have my program in the same directory it's working now.