Level Extreme platform
Subscription
Corporate profile
Products & Services
Support
Legal
Français
Redirect STDERR to nul
Message
From
10/07/2012 14:13:58
 
 
To
10/07/2012 10:38:42
General information
Forum:
ASP.NET
Category:
Other
Environment versions
Environment:
VB 9.0
OS:
Windows 7
Network:
Windows 2003 Server
Database:
MS SQL Server
Application:
Web
Miscellaneous
Thread ID:
01548039
Message ID:
01548091
Views:
47
Here is the class that will handle it:
Imports System.Text
Imports System.Threading

Namespace Framework

    Public Class ProcessFile

        Public cArguments As String = ""
        Public cDomain As String = ""
        Public cError As String = ""
        Public cFileName As String = ""
        Public cLog As String = ""
        Public cMessage As String = ""
        Public cOutput As String = ""
        Public cPassword As String = ""
        Public cUsername As String = ""
        Public cWorkingDirectory As String = ""
        Public lReadError As Boolean = True
        Public lRedirectStandardError As Boolean = True
        Public lRedirectStandardInput As Boolean = True
        Public lRedirectStandardOutput As Boolean = True
        Private oApp As Framework.App = Nothing
        Private oLXProcess As Framework.LXProcess = Nothing
        Private oLogMutex As Mutex = New Mutex()
        Private oStringBuilderError As StringBuilder = New StringBuilder
        Private oStringBuilderOutput As StringBuilder = New StringBuilder

        ' This is when we access the class in a desktop mode
        Public Sub New(ByVal toApplication As Framework.App)
            oApp = toApplication
        End Sub

        ' This is when we access the class in a Web or Web Service mode
        Public Sub New(ByVal toProcess As Framework.LXProcess)
            oLXProcess = toProcess
            oApp = oLXProcess.oApp
        End Sub

        ' As you have already discovered, especially when used within an app, the WinZip Command Line Add-on will likely encounter
        ' an error discerning the handle if STDOUT is being redirected while STDERR is not being redirected.
        ' I have not seen any issue with refraining from redirecting the input.
        ' I am not qualified to advise you on programming in .NET, but if you can direct the STDERR pipe to some sort of null
        ' file while redirecting STDOUT to something useful, you should be able to avoid this issue.
        ' If you redirect using ">" you are only redirecting the STDOUT pipe. In the DOS syntax, you would need to use something similar to:
        ' wzzip file.zip [files] 1>out.txt 2>NUL
        ' The first (1>out.txt) would be the redirection of STDOUT containing useful information and the second (2>NUL) would be the 
        ' redirection of STDERR containing only garbage information, such as the "dots" used as a type of progress meter.

        ' Execute an executable
        Public Function Process() As Boolean
            Dim loSecureString As System.Security.SecureString = New System.Security.SecureString()
            Dim lnCounter As Integer = 0

            ' Reset the values
            cError = ""
            cLog = ""
            cMessage = ""
            cOutput = ""

            ' Trim everything
            cArguments = Trim(cArguments)
            cDomain = Trim(cDomain)
            cPassword = Trim(cPassword)
            cUsername = Trim(cUsername)

            ' If we have a working directory
            If cWorkingDirectory.Length > 0 Then

                ' If the directory does not exist
                If Not oApp.DirectoryExist(cWorkingDirectory) Then
                    cMessage = "The directory " + cWorkingDirectory + " does not exist."
                    Return False
                End If

            End If

            ' If the file does not exist
            If Not oApp.FileExist(cFileName) Then
                cMessage = "The file " + cFileName + " does not exist."
                Return False
            End If

            ' Use the Using\End Using approach to make sure the memory is released no matter what. This covers an unexpected error
            ' before it reaches the end. It closes the resources automatically at the end.
            Using loProcess As New Process

                loProcess.StartInfo.FileName = cFileName
                loProcess.StartInfo.WorkingDirectory = cWorkingDirectory
                loProcess.StartInfo.Arguments = cArguments
                loProcess.StartInfo.RedirectStandardOutput = lRedirectStandardOutput

                ' Stream the output to an event
                AddHandler loProcess.OutputDataReceived, AddressOf OutputDataHandler

                loProcess.StartInfo.RedirectStandardInput = lRedirectStandardInput
                loProcess.StartInfo.RedirectStandardError = lRedirectStandardError

                ' Stream the error to an event
                AddHandler loProcess.ErrorDataReceived, AddressOf ErrorDataHandler

                loProcess.StartInfo.UseShellExecute = False
                loProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
                loProcess.StartInfo.CreateNoWindow = True

                ' If we have a domain
                If cDomain.Length > 0 Then
                    loProcess.StartInfo.Domain = cDomain
                End If

                ' If we have a username
                If cUsername.Length > 0 Then
                    loProcess.StartInfo.LoadUserProfile = True
                    loProcess.StartInfo.UserName = cUsername

                    ' For each character
                    For lnCounter = 1 To cPassword.Length
                        loSecureString.AppendChar(cPassword(lnCounter - 1))
                    Next

                    loProcess.StartInfo.Password = loSecureString
                End If

                Try
                    loProcess.Start()
                Catch loError As Exception
                    cMessage = loError.Message + " " + cFileName + " " + cArguments
                    Return False
                End Try

                ' Start the asynchronous read of the error stream
                loProcess.BeginErrorReadLine()

                ' Start the asynchronous read of the output stream
                loProcess.BeginOutputReadLine()

                ' Wait for the process to complete before proceeding
                loProcess.WaitForExit()

                ' Dump the log to the error
                cError = oStringBuilderError.ToString

                ' Dump the log to the output
                cOutput = oStringBuilderOutput.ToString

                ' If we have an output
                If cOutput.Length > 0 Then

                    ' If we have at least three characters
                    If cOutput.Length > 2 Then

                        ' If the last characters is a carriage return
                        If Mid(cOutput, cOutput.Length - 1, 2) = oApp.cCR Then
                            cOutput = Mid(cOutput, 1, cOutput.Length - 2)
                        End If

                    End If

                    ' Some output contains only CHR(13), so we will do some parsing to standardize all this
                    cOutput = oApp.StrTran(cOutput, oApp.cCR, "LXFramework" + Chr(0) + "LXFramework")
                    cOutput = oApp.StrTran(cOutput, Chr(13), oApp.cCR)
                    cOutput = oApp.StrTran(cOutput, "LXFramework" + Chr(0) + "LXFramework", oApp.cCR)

                End If

                ' If we have an output
                If cOutput.Length > 0 Then
                    cLog = cLog + "Output" + oApp.cCR + "------" + oApp.cCR + cOutput
                End If

            End Using

            ' Reset the values
            cDomain = ""
            cPassword = ""
            cUsername = ""

            Return True
        End Function

        ' Collect the output, displaying it to the screen and logging it to the output file
        ' Cancel the read operation when the maximum line limit is reached
        Private Sub OutputDataHandler(toProcess As Object, toOutLine As DataReceivedEventArgs)

            ' If we have something to write
            If Not String.IsNullOrEmpty(toOutLine.Data) Then
                oLogMutex.WaitOne()

                ' Add to the log
                oStringBuilderOutput.Append(toOutLine.Data + oApp.cCR)

                oLogMutex.ReleaseMutex()
            End If

        End Sub

        ' Collect the error output, displaying it to the screen and logging it to the output file
        ' Cancel the error output read operation when the maximum line limit is reached
        Private Sub ErrorDataHandler(toProcess As Object, toErrLine As DataReceivedEventArgs)

            ' If we have something to write
            If Not String.IsNullOrEmpty(toErrLine.Data) Then
                oLogMutex.WaitOne()

                ' Add to the log
                oStringBuilderError.Append(toErrLine.Data + oApp.cCR)

                oLogMutex.ReleaseMutex()
            End If

        End Sub

    End Class

End Namespace
Some of it contains some framework related references but this should be pretty much easy to follow. This approach comes initially from MSDN where the situation is that both streams (output and error) have to be dealt with separately. So, each has its own thread. Thus, this approach eliminates the conflict with some utilities, such as WZZip.exe, when executed from an app, such as here, will cause a freeze during the zip when it reaches about 13.2 MB. This is a bug confirmed by WinZip and so far that is the only workaround. However, having done it, the adjustment, makes the class universal for any other related situations.

As far as WZZip.exe, the issue is that they recommend to send STDERR to null. But, simply using 2>NUL at the Process arguments won't be enough. This has to be done at the threading level. So, the STDERR still works, even if it returns some very unexpected output such as this:
[10/07/2012 13:25:49] StdOut> WinZip(R) Command Line Support Add-On Version 3.2 (Build 9715)
[10/07/2012 13:25:49] StdOut> Copyright (c) 1991-2011 WinZip International LLC - All Rights Reserved
[10/07/2012 13:25:49] StdOut>   Adding 0005504N6403.prn
[10/07/2012 13:25:49] StdOut>   Adding 0005504N640301.dat
[10/07/2012 13:25:49] StdOut>   Adding 0005504N640301.jpg
[10/07/2012 13:25:50] StdOut>   Adding 0005504N640302.jpg
[10/07/2012 13:25:50] StdOut>   Adding 0005504N640303.jpg
[10/07/2012 13:25:51] StdOut>   Adding 0005504N640304.jpg
[10/07/2012 13:25:51] StdOut>   Adding 0005504N640305.jpg
[10/07/2012 13:25:51] StdOut>   Adding 0005504N640306.jpg
[10/07/2012 13:25:52] StdOut>   Adding 0005504N640307.jpg
[10/07/2012 13:25:52] StdOut>   Adding 0005504N640308.jpg
[10/07/2012 13:25:52] StdOut>   Adding 0005504N640309.jpg
[10/07/2012 13:25:52] StdOut>   Adding 0005504N640310.jpg
[10/07/2012 13:25:52] StdOut>   Adding 0005504N640311.jpg
[10/07/2012 13:25:52] StdOut>   Adding 0005504N640312.jpg
[10/07/2012 13:25:52] StdOut>   Adding 0005504N640313.jpg
[10/07/2012 13:25:52] StdOut>   Adding 0005504N640314.jpg
[10/07/2012 13:25:52] StdOut>   Adding 0005504N640315.jpg
[10/07/2012 13:25:52] StdOut>   Adding 0005504N640316.jpg
[10/07/2012 13:25:52] StdOut>   Adding 0005504N640317.jpg
[10/07/2012 13:25:52] StdOut>   Adding 0005504N640318.jpg
[10/07/2012 13:25:52] StdOut>   Adding 0005504N640319.jpg
[10/07/2012 13:25:52] StdOut>   Adding 0005504N640320.jpg
[10/07/2012 13:25:53] StdOut>   Adding 0005504N640321.jpg
[10/07/2012 13:25:53] StdOut>   Adding 0005504N640322.jpg
[10/07/2012 13:25:53] StdOut>   Adding 0005504N640323.jpg
[10/07/2012 13:25:53] StdOut>   Adding 0005504N640324.jpg
[10/07/2012 13:25:53] StdOut>   Adding 0005504N640325.jpg
[10/07/2012 13:25:53] StdOut>   Adding 0005504N640326.jpg
[10/07/2012 13:25:53] StdOut>   Adding 0005504N640327.jpg
[10/07/2012 13:25:53] StdOut>   Adding 0005504N640328.jpg
[10/07/2012 13:25:53] StdOut> Creating Zip file h:\test.zip
[10/07/2012 13:25:53] StdErr> Searching...            ...   ..  .....     ..  .......       ..  ...............................     
As you can see, during its package, when it builts it up, closing around 13.2 MB will have the exe switch into a StdErr mode waiting or searching indefinitely for something, which is the problem, a bug confirmed by WinZip, and this takes care of it.
Michel Fournier
Level Extreme Inc.
Designer, architect, owner of the Level Extreme Platform
Subscribe to the site at https://www.levelextreme.com/Home/DataEntry?Activator=55&NoStore=303
Subscription benefits https://www.levelextreme.com/Home/ViewPage?Activator=7&ID=52
Previous
Next
Reply
Map
View

Click here to load this message in the networking platform