# Visual Basic > Visual Basic .NET >  Process - Capturing output and ALSO displaying window

## BitFlipper88

Hi,

I am building a function to open a process with arguments, and return the output of that process (note: its a 7Zip CMD line execution). 

Additionally, I want to view the console live (for any debug purposes).

So, I thought that using the process object, I might be abled to achieve both requirements of this function. And my code is....





```
                    ProcInfo = New ProcessStartInfo(ExePath, ArgString)


                With ProcInfo
                    .UseShellExecute = False         'required for re-direct
                    .RedirectStandardOutput = True  'TRUE DISABLES WINDOW
                    .RedirectStandardError = True   'TRUE DISABLES WINDOW

                    If ShowWindow = True Then
                        .CreateNoWindow = False
                        .WindowStyle = ProcessWindowStyle.Normal
                    Else
                        .CreateNoWindow = True
                    End If
                End With

                Proc.StartInfo = ProcInfo
                Proc.Start()
```

Unfortunately, re-directing the output... does just that. And prevents the console window output from being viewed.


Albeit, I'm no stranger to this task. In VBA I wrote a beautiful function that achieves this by running CMD.exe with provided arguments and pipes the output to a text file, reads it back and wah-lah. A bit of a 'hacky' solution, but works.

So, I attempted a similar function in .NET with the Process object and it continually resulted in CMD.exe not parsing the arguments correctly (unrecognizable command). Wrapping chr(34) around arguments and the string, blah blah. I'm done fiddling with it.

In conclusion, I was wondering if anyone had any other method besides using CMD.exe to pipe the output?

Thanks in advance!

----------


## jmcilhinney

> and wah-lah


It's "voila". It's a French word that means "there it is".

----------


## jmcilhinney

Have you considered displaying the console output in your own app? If it's only while debugging, you can call Debug.WriteLine and it will be written to the Output window in VS and that code will be ignored in a Release build. Alternatively, you could write it to a read-only, multiline TextBox.

----------


## BitFlipper88

> Have you considered displaying the console output in your own app? If it's only while debugging, you can call Debug.WriteLine and it will be written to the Output window in VS and that code will be ignored in a Release build. Alternatively, you could write it to a read-only, multiline TextBox.


I have considered that I can display the captured output from the function. But allowing the ability to also display a console is a feature I hoped to implement in this function, and also one that existed in my VBA function mentioned in OP. While this sounds trivial, I like to build tools with versatility. 

If this requires launching CMD.exe and piping output, I may revisit that approach, but frankly I'm over figuring out how to please CMD.exe's argument structure. 


Thanks

----------


## jmcilhinney

> If this requires launching CMD.exe and piping output, I may revisit that approach, but frankly I'm over figuring out how to please CMD.exe's argument structure.


There's not all that much to it so I suggest that you show us what you were trying to do and we'll see if we can figure that out. Although it's not a guarantee that none exists, I'm not aware of any way to do what you're asking for.

----------


## BitFlipper88

Down the rabbit hole I go....

So i've racked my brain considerably on these functions. First allow me to re-iterate design requirements:

The function should...

1.)  Run a CMD with arguments
2.)  The arguments should be whitespace friendly (i.e. whitespaces in paths do not break the CMD structure)
3.)  The output of the command should be visible in real-time during execution
4.)  A return value of the commands output should be provided to the caller (.Net)

If 3 and 4 are asynchronous, then so be it. 

First, I have two functions to attempt this functionality. The first _RunCMD_, uses the _Shell_ function to execute CMD.exe. Providing whitespaces in path arguments here requires the use of the 'LITERAL = FALSE' flag, which encapsulates all arguments with chr(34). Note that the Shell function does not offer any way to return the output. This is possible through use of a pipe to text file and read back to VB. 

Second, is _RunFile_, which makes use of the Process object. This object inherently handles whitespaces characters in arguments. Object also allows output re-direct, which consequently disables viewing the command execution.
*Edit:  This object does NOT handle whitespaces in arguments. It seems to do a better job with it than Shell though. (The .exe path has a space in 'Program Files'. And the Process object handles this fine.). However, further testing indicates whitespaces in argument paths are a problem. I've updated code for a 'LITERAL=False' parameter to RunFile().*




Both of these functions at the current time have ISSUES and I will highlight them below.

Note that I am testing these functions with the 7Zip command line tool. The command is to take a folder and convert it 7z archive.

First, the code....

*Test routine...*


```
        Dim filname As String = SelectFolder()

        Dim Args As New List(Of String)
        With Args
            .Add("C:\Program Files\7-Zip\7z.exe")
            .Add("a") 'build archive switch
            .Add("-pMyPassword") 'password
            .Add("-mhe=on") 'encrypt header
            .Add("-mmt=8")
            .Add("-mx=1")
            .Add("-bsp2")   'display progress - comment out to cause RunCMD to return no output and application is QUIT?
            .Add(filname & ".7z")  'destination archive name
            .Add(filname)  'source folder to zip
        End With

        MsgBox("RunCMD():" & vbCrLf & RunCMD(Args, False, ProcessWindowStyle.Normal))  'hidden does nothing apparently.


        Args.RemoveRange(0, 1) 'remove executable form CMD args...
        MsgBox("RunFile():" & vbCrLf & RunFile("C:\Program Files\7-Zip\7z.exe", Args, False))
```

*RunCMD()*


```
        Shared Function RunCMD(Args As List(Of String), Optional LITERAL As Boolean = False,
                               Optional WindowMode As ProcessWindowStyle = ProcessWindowStyle.Normal,
                               Optional KeepOpen As Boolean = False) As String
            'BitFlipper88 @ VBForums - 12-30-22
            Dim Proc As New Process
            Dim ProcInfo As ProcessStartInfo
            Dim ArgString As String
            Dim TempFile As String

            'ASSEMBLE ARG STRING
            For Each arg In Args
                If Not LITERAL Then arg = Chr(34) & arg & Chr(34)
                ArgString = ArgString & " " & arg
            Next
            ArgString = Trim(ArgString)

            'MAKE OUTPUT FILE
            TempFile = Path.Combine(Path.GetTempPath(), "VBNET_RUNCMD_OUTPUT.txt")
            File.Create(TempFile).Dispose()

            'BUILD PIPE COMMAND
            ArgString = ArgString & " > " & Chr(34) & TempFile & Chr(34)

            'ADD CMD SWITCHES
            If KeepOpen = True Then
                ArgString = "/K " & Chr(34) & ArgString & Chr(34) 'Keep console open after command executes
            Else
                ArgString = "/C " & Chr(34) & ArgString & Chr(34)  'Close console after execute
            End If

            Debug.Print("CMD RGS = " & ArgString)

            'NOTE:  When KeepOpen = True, the application will exit directly after shell command is finished.
            ' Thereby preventing ability to read piped output.
            If KeepOpen = True Then RunCMD = "" '<--- trying to establish a default return value if shell quits the application? 

            'SHELL FUNCTION ALLOWS TO VIEW OUTPUT BEFORE IT PIPES
            Return Shell("cmd.exe " & ArgString, WindowMode, True)

            'READ OUTPUT - NOTE IF 'KeepOpen' = TRUE then, application exits after shell command
            If Not File.Exists(TempFile) Then
                Throw New System.Exception("RunCMD: Failed. Expected output.")
            Else
                Return File.ReadAllText(TempFile)
                File.Delete(TempFile) 'cleanup
            End If
        End Function
```


*RunFile()*


```

        Shared Function RunFile(ExePath As String, Optional Args As List(Of String) = Nothing,
                                Optional LITERAL As Boolean = False, Optional ShowWindow As Boolean = True) As String
            'OPENS A FILE, OPTIONAL PASS OPEN ARGUMENTS
            'SEE RunCMD() FOR RUNNING WINDOWS COMMANDS.
            'WILL WAIT UNTIL COMMAND COMPLETES

            'BitFlipper88 @ VBForums 12-30-22
            Dim Proc As New Process
            Dim ProcInfo As ProcessStartInfo
            Dim ArgString As String
            Dim Output As String

            Try
                If Not Args Is Nothing Then
                    For Each arg As String In Args
                        If Not LITERAL Then
                            ArgString = ArgString & " " & Chr(34) & arg & Chr(34)
                        Else
                            ArgString = ArgString & " " & arg
                        End If
                    Next
                    ArgString = Trim(ArgString)
                    If ArgString = "" Then Throw New System.Exception("RunFile(): Error. Argument list passed is empty.")
                End If

                ProcInfo = New ProcessStartInfo(ExePath, ArgString)
                Debug.Print("RUN FILE ARGS:  " & ArgString)
                With ProcInfo
                    .UseShellExecute = False         'required for re-direct
                    .RedirectStandardOutput = True  'TRUE DISABLES WINDOW
                    .RedirectStandardError = True   'TRUE DISABLES WINDOW

                    If ShowWindow = True Then
                        .CreateNoWindow = False
                        .WindowStyle = ProcessWindowStyle.Normal
                        .RedirectStandardOutput = False  'WINDOW ENABLED
                        .RedirectStandardError = False   'WINDOW ENABLED
                    End If

                End With

                Proc.StartInfo = ProcInfo
                Proc.Start()

                Proc.WaitForExit() 'THIS PROP WAITS FOR THE APPLICATION TO QUIT ITSELF OR BE QUIT BY A USER

                If ShowWindow = True Then
                    Return "RunFile(): Function output disabled with ShowWindow=TRUE"
                Else
                    Dim SReader As System.IO.StreamReader = Proc.StandardOutput
                    Output = SReader.ReadToEnd
                    Dim SError As System.IO.StreamReader = Proc.StandardError
                    Output = Output & vbCrLf & SError.ReadToEnd
                    SReader.Close()
                    Return Output
                End If

            Catch ex As Exception
                Return "RunFile(): EXCEPTION - " & ex.Message
            End Try

        End Function
```


Now, with RunCMD().... First, there is no way to hide the console. Whether I pass the windowstyle of hidden, normal, etc to the _Shell_ function, it makes no difference.
How wonderful! So, this function cannot be used if desiring to run the command "silently". I do find it interesting how even a PIPED command will show the 7zip progress percentage update in real time. I would think that CMD.exe would consider this a single command and thus would not see anything. 

RunFile.... This will run silently and return the complete output. You cannot, however, show the window, while also returning an output. As the redirect options disables the ability to view the console. 

As for BOTH.... NEITHER function returns anything when the execution of the command window is exited prematurely. Both the _Process_ object and the _Shell_ function kills the application stack when the window is exited. Note this is also true when *KeepOpen=TRUE* on _RunCMD()_ because the user must close the window... I find this behavior to be absolutely absurd. Perhaps this is due to my environment being a console app? (p.s. Is there actually a project type I can just run code in and test it without a console? - I don't need a console to write code!)
*Edit:  In a clearer mind:  The obvious fact occurred to me that the application is going to quit entirely and not return a value if the console window is closed... because my application is a console app. Stupid me... I guess I don't understand why the shell can't prompt a fresh instance of CMD.exe window - outside of my environment??? Again, I don't want a console app to test code!* 

To conclude, this is where I'm at. Perhaps I can't view the command window live and also return the output to .NET. That is fine I suppose... But that would be ideal. 

I will leave here for the time being. If I make any headway, I will report back.
Thanks for the help.

----------


## .paul.

You just need to use the process class to run Cmd.exe. No need for a console app
What are the exact command line arguments youd pass to cmd if you were doing it manually?

----------


## .paul.

To keep a console window open, you just need to pass another Console.ReadLine

----------

