We help IT Professionals succeed at work.

Console output

CJ_S
CJ_S asked
on
I am trying to redirect the output generated by the console window to a file, and read it out. the following is what I have so far, and it works. It just does not write out the command to the file. Can anybody help me out a bit, it's probably just a small thing...

Private Const NORMAL_PRIORITY_CLASS = &H20
Private Const STARTF_USESTDHANDLES = &H100
Private Const STARTF_USESHOWWINDOW = &H1
Private Const STILL_ACTIVE = &H103
Private Const CREATE_NEW_CONSOLE = &H10
Private Const INFINITE = &HFFFF

Private Const GENERIC_READ = &H80000000
Private Const GENERIC_WRITE = &H40000000
Private Const FILE_SHARE_READ = &H1
Private Const FILE_SHARE_WRITE = &H2
Private Const CREATE_ALWAYS = 2
Private Const CREATE_NEW = 1
Private Const OPEN_ALWAYS = 4
Private Const OPEN_EXISTING = 3
Private Const TRUNCATE_EXISTING = 5
Private Const FILE_ATTRIBUTE_ARCHIVE = &H20
Private Const FILE_ATTRIBUTE_HIDDEN = &H2
Private Const FILE_ATTRIBUTE_NORMAL = &H80
Private Const FILE_ATTRIBUTE_READONLY = &H1
Private Const FILE_ATTRIBUTE_SYSTEM = &H4
Private Const FILE_FLAG_DELETE_ON_CLOSE = &H4000000
Private Const FILE_FLAG_NO_BUFFERING = &H20000000
Private Const FILE_FLAG_OVERLAPPED = &H40000000
Private Const FILE_FLAG_POSIX_SEMANTICS = &H1000000
Private Const FILE_FLAG_RANDOM_ACCESS = &H10000000
Private Const FILE_FLAG_SEQUENTIAL_SCAN = &H8000000
Private Const FILE_FLAG_WRITE_THROUGH = &H80000000

Private Type SECURITY_ATTRIBUTES
        nLength As Long
        lpSecurityDescriptor As Long
        bInheritHandle As Long
End Type


Private Type STARTUPINFO
        cb As Long
        lpReserved As String
        lpDesktop As String
        lpTitle As String
        dwX As Long
        dwY As Long
        dwXSize As Long
        dwYSize As Long
        dwXCountChars As Long
        dwYCountChars As Long
        dwFillAttribute As Long
        dwFlags As Long
        wShowWindow As Integer
        cbReserved2 As Integer
        lpReserved2 As Long
        hStdInput As Long
        hStdOutput As Long
        hStdError As Long
End Type

Private Type PROCESS_INFORMATION
        hProcess As Long
        hThread As Long
        dwProcessId As Long
        dwThreadId As Long
End Type

Private Declare Function CreateProcess Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, lpProcessAttributes As Any, lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, ByVal lpCurrentDriectory As String, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As Long, lpExitCode As Long) As Long

Private Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (ByVal lpFileName As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
Private Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long
Private Declare Function DeleteFile Lib "kernel32" Alias "DeleteFileA" (ByVal lpFileName As String) As Long

Private Declare Function lread Lib "kernel32" Alias "_lread" (ByVal hFile As Long, lpBuffer As Any, ByVal wBytes As Long) As Long
Private Declare Function lopen Lib "kernel32" Alias "_lopen" (ByVal lpPathName As String, ByVal iReadWrite As Long) As Long
Private Declare Function lclose Lib "kernel32" Alias "_lclose" (ByVal hFile As Long) As Long

Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Declare Function GetLastError Lib "kernel32" () As Long
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long

Private Function StartProcess(ByVal sProgram As String) As String
    Dim stInfo As STARTUPINFO
    Dim prInfo As PROCESS_INFORMATION
    Dim sTmpFile As String
    sTmpFile = App.Path & "\test2.cof"
   
    stInfo.cb = Len(stInfo)
    stInfo.lpReserved = vbNull
    stInfo.wShowWindow = 5
    stInfo.cbReserved2 = 0
    stInfo.lpReserved2 = vbNull
    stInfo.dwFlags = STARTF_USESTDHANDLES Or STARTF_USESHOWWINDOW
       
    Dim hFile As Long
    hFile = CreateFile(sTmpFile, GENERIC_READ Or GENERIC_WRITE, 0, ByVal CLng(0), CREATE_ALWAYS, 0, ByVal CLng(0))
   
    If hFile Then
        stInfo.hStdOutput = hFile
    End If
   
    Dim ret As Long, ExitCode As Long, lProcessTime As Long
    lProcessTime = 0
   
    ret = CreateProcess(sProgram, vbNullString, ByVal CLng(0), ByVal CLng(0), True, CREATE_NEW_CONSOLE, ByVal CLng(0), vbNullString, stInfo, prInfo)
    If (ret = 0) Then MsgBox "Not created!"
    Do
        GetExitCodeProcess prInfo.hProcess, ExitCode
   
        lProcessTime = lProcessTime + 500
        Sleep 500 '1000 = 1 second.
        If (lProcessTime > 20000) Then
            MsgBox "Processing exceeds time limit!"
            GoTo closeHandler
        End If
    Loop While ExitCode = STILL_ACTIVE
    CloseHandle prInfo.hProcess
   
   
    If hFile Then
        Dim numOfBytes As Long
        Dim buf
        numOfBytes = GetFileSize(hFile, ByVal 0&)
        CloseHandle (hFile)
        If (numOfBytes >= 0) Then
            buf = String(numOfBytes, "*")

            hFile = lopen(sTmpFile, 0)
            lread hFile, buf, numOfBytes
            lclose (hFile)
   
            ' Returns
            StartProcess = buf
        End If
        'DeleteFile (sTmpFile)
    End If
   
    Exit Function
closeHandler:
   

End Function


Public Function DoCommand(ByVal sCommand As String) As String
    MsgBox StartProcess("c:\winnt\system32\ping.exe")
End Function
Comment
Watch Question

Debugging shows that a valid file handle is created and output should be redirected to that file, yes?  What OS are you running?

Author

Commented:
I am running Windows 2000 Advanced Server.

I too found nothing that could be the problem. I also tried it with CreatePipe, but that won't work either...

CJ
Yea, I think I tried very similar code to the above using the sample at VB2theMax by Dino Esposito:

"Capturing the Output of a MS-DOS Program"
http://www.vb2themax.com/HtmlDoc.asp?Table=Articles&ID=40


It didnt work, and I emailed him.  He claimed he could offer no support for why it did not work on my machine (Windows 2000 Professional).  I don't know what has been altered with the OS, but it looks like it may be the problem.  I got around it using another method (can't recall off hand what it was right now), but I will look through my files when I get home and see if I can find it.

Author

Commented:
I'd appreciate that very much!

CJ
It probably has some bugs, but hopefully its enough to go on, and is commented well enough for you to understand.  It was written specifically to work on Win2k so if you're looking for cross OS functionality, it'll need a little tweaking.  Any questions, let me know.  It was written as a class:




Option Explicit

Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As Long, lpExitCode As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function GetTempPath Lib "kernel32" Alias "GetTempPathA" (ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long
Private Declare Function GetTempFileName Lib "kernel32" Alias "GetTempFileNameA" (ByVal lpszPath As String, ByVal lpPrefixString As String, ByVal wUnique As Long, ByVal lpTempFileName As String) As Long
Private Declare Function GetShortPathName Lib "kernel32" Alias "GetShortPathNameA" (ByVal lpszLongPath As String, ByVal lpszShortPath As String, ByVal lBuffer As Long) As Long


Private Const PROCESS_QUERY_INFORMATION = &H400
Private Const STATUS_PENDING = &H103&
Private Const MAX_PATH = 260


Public Enum OMETHOD
    None = 0
    ToFile = 1
    ReturnString = 2
End Enum




'*********************************************************************************
'Function Name  :   Execute
'Purpose        :   Execute command line functions
'
'Parameters     :   ProgramPath             - path to program (leave blank for DOS commands)
'                   CommandLineParameters   - any parameters to be passed to program
'                                               (for DOS commands place entire string here)
'                   WaitForTermination      - wait for completion of command before
'                                               returning back to calling app
'                   OutputMethod            - method by which output is to be rendered
'                   OutputPath              - path of file to write output to
'
'Return Values  :   string of output if specified
'*********************************************************************************


Public Function Execute(ProgramPath As String, _
            Optional CommandLineParameters As String = "", _
            Optional WaitForTermination As Boolean = False, _
            Optional OutputMethod As OMETHOD = None, _
            Optional OutputPath As String = "") As String
           
   
    Dim lret            As Long
    Dim lFile           As Long
    Dim hProc           As Long
    Dim exitCode        As Long
    Dim strBuff         As String
    Dim strTempFile     As String
    Dim strFullCommand  As String
   

   
    'if redirect to file and no output file is specified, change to return string
    If OutputMethod = ToFile And Trim$(OutputPath) = "" Then OutputMethod = ReturnString
       
    If OutputMethod = ReturnString Then
        OutputPath = GetTempFile
    End If
   
   
    'create the temp file
    If OutputMethod <> None Then
        lFile = FreeFile
        Open OutputPath For Output As #lFile
        Close #lFile
       
        'convert path to short path
        OutputPath = GetShortPath(OutputPath)
    End If
   
   
    'get the short path to the program
    ProgramPath = GetShortPath(ProgramPath)
   
    'create the full command line string
    strFullCommand = " /c " & LTrim$(ProgramPath) & " " & LTrim$(CommandLineParameters)
   
   
    'create the process
    If OutputMethod = None Then
        lret = Shell(Environ("COMSPEC") & strFullCommand, vbHide)
    Else
        lret = Shell(Environ("COMSPEC") & strFullCommand & " > " & OutputPath, vbHide)
    End If
   

   
    'if option to wait, then wait
    If lret And WaitForTermination Then
        hProc = OpenProcess(PROCESS_QUERY_INFORMATION, False, lret)
       
        Do
            Call GetExitCodeProcess(hProc, exitCode)
            DoEvents
        Loop While exitCode = STATUS_PENDING

        Call CloseHandle(hProc)
    End If
   
   
    'return string if applicable
    If OutputMethod = ReturnString Then
        lFile = FreeFile
        Open OutputPath For Binary As #lFile
        strBuff = Space$(LOF(lFile))
        Get #lFile, , strBuff
        Close #lFile
       
        'kill the temp file
        Kill OutputPath
       
        Execute = strBuff
    End If
   
End Function




Private Function GetTempFile() As String

    Dim strTempFile     As String
    Dim strTempFolder   As String
    Dim ret             As Long
   
   
    strTempFile = Space$(MAX_PATH)
   
    '
    'find the temporary folder
    '
    strTempFolder = Space$(MAX_PATH)
    ret = GetTempPath(MAX_PATH, strTempFolder)
    strTempFolder = Left$(strTempFolder, ret)
   
   
    '
    'create the temp file
    '
    ret = GetTempFileName(strTempFolder, "EXE", 0, strTempFile)
   
    If ret <> 0 Then
        strTempFile = Left$(strTempFile, InStr(1, strTempFile, Chr$(0)) - 1)

        GetTempFile = strTempFile
    End If
End Function



Private Function GetShortPath(strFileName As String) As String

    Dim lpos As Long
    Dim strPath As String
   
    strPath = Space$(255)
    lpos = GetShortPathName(strFileName, strPath, Len(strPath))
    GetShortPath = Left$(strPath, lpos)

End Function

Author

Commented:
I like the code Azra (just read through it) I will try it in a while. There's just one thing that is bothering me a little bit. And that is the use of the > to redirect the output to a file. It is exactly the same as my first attempt, but wouldn't work in my scenario. I'll see what I can do with the above bit of code. Thnx in advance, and I'll get back to you!

Regards,
CJ

Author

Commented:
I just tried the code, and it works fine for ping-commands. It does not work for net.exe which is why I am creating the program :-(

Maybe you know why?

CJ
What specific command is giving you problems?  For example, I tried:

Text1.Text = c.Execute("", "net view \\compname", True, ReturnString)

And it returned the proper string.  I also tried it with file redirection and it wrote to the file.

Author

Commented:
"", "c:\winnt\system32\net.exe", True, ReturnString

It should show me the help from net.exe, but it doesn't.

There's also another thing that I would need to do, but that can wait for another question, and I still have to try the terminateprocess api, and a couple of others.

Reason that I am using the full command to net.exe is because on the actual server those commands are placed in another directory... just in case you wonder :-)

CJ

Author

Commented:

Author

Commented:
cleaning up