Capture Console Output

Started by Paul Squires, March 05, 2012, 09:18:34 PM

Previous topic - Next topic

Paul Squires

One of the hardest things to do reliably is capture the text output from a console application. I have been using modified VB code and it worked "okay", but sometimes it would hang, etc.

I am working with Fossil (http://www.fossil-scm.org) to see if I can add source code version control to FireFly and I need to capture the console screen output. My code was hit and miss and seemed to miss more often then it should. I simply could not rely on it.

I searched the Net for a solution and came across a great CodeProject project that does exactly what I want. It is supplied in DLL form and can be easily called. Here is the link: http://www.codeproject.com/Articles/32965/CaptureConsole-DLL-A-Universal-Console-Output-Redi

I have been using it non-stop over the past few days and it works perfectly every time.

Here is the declare for the one function that you call in the DLL (you use unicode variables when calling the function):


Declare Function ExecuteW Lib "CaptureConsole" Alias "ExecuteW" ( _
                     ByVal s_CommandLine    As wString, _
                     ByVal s32_FirstConvert As Long, _
                     ByVal s_CurrentDir     As Long, _
                     ByVal s_Environment    As Long, _
                     ByVal b_SeparatePipes  As Long, _
                     ByVal s32_Timeout      As Long, _
                     ByRef s_ApiError       As wString, _
                     ByRef s_StdOut         As wString, _
                     ByRef s_StdErr         As wString _
                     ) As Long


Hopefully this will be of use to some of you guys as well.  :)
Paul Squires
PlanetSquires Software

José Roca

Your declare looks more like VB than PB :)


'typedef DWORD (WINAPI* tExecute)(const WCHAR*, DWORD, const WCHAR*, const WCHAR*, BOOL, DWORD, BSTR*, BSTR*, BSTR*);

Declare Function ExecuteW Lib "CaptureConsole" Alias "ExecuteW" ( _
                     ByRef s_CommandLine    As wStringZ, _
                     ByVal s32_FirstConvert As Dword, _
                     ByRef s_CurrentDir     As wStringZ, _
                     ByRef s_Environment    As wStringZ, _
                     ByVal b_SeparatePipes  As Long, _
                     ByVal s32_Timeout      As DWord, _
                     ByRef s_ApiError       As wString, _
                     ByRef s_StdOut         As wString, _
                     ByRef s_StdErr         As wString _
                     ) As DWord


Pedro Marquez

You can teach an example use of the function,

Paul Squires

You need to download the console package and use the 32-bit DLL. Put that DLL in the same folder as your EXE.

In the project I created, I was using a function like the following to call the ExecuteW in the DLL.


'--------------------------------------------------------------------------------
Function ExecuteConsoleApp( wCommandLine As wString, _
                            wResult      As wString _
                            ) As Long 

   Local wApiError As wString
   Local wStdOut   As wString
   Local wStdErr   As wString

   Function = ExecuteW( wCommandLine, 0, 0, _
                         1, 0, 120000, _
                         wApiError, _
                         wStdOut, _
                         wStdErr )
   
   wResult = wStdOut
                         
End Function


Example:

   ' Create the Fossil source code repository
   Local wst     As wString
   Local wResult As wString
   
   wst = gFossilExe & " init " & gFossilDB 

   ExecuteConsoleApp wst, wResult

   ShowResultW wResult

Paul Squires
PlanetSquires Software

Pedro Marquez

Thanks, but do not want to use external dll

Elias Montoya


Pedro,

You can execute your console app via CreateProcess(), making sure to set the process info like this:

   pi.cb          = SizeOf (NewProcStartInfo)
   pi.dwFlags     = %STARTF_USESTDHANDLES
   pi.wShowWindow = %Null
   pi.hStdInput   = hStdOutPut
   pi.hStdOutput  = hStdInPut
   pi.hStdError   = hStdErr
   pi.lpTitle     = %Null


This redirects the output of STDOUT to the files that are opened with those 3 file handles (need to create and open the files yourself).

Then just use WaitForSingleObject() for the app to finish, and then open the contents of StdIN file, the contents of the output should be there. Im sorry i dont have an example right now, but this is the idea and you can search for examples using those terms. :)


Win7, iMac x64 Retina display 5K, i7-5820K 4.4 ghz, 32GB RAM, All updates applied. - Firefly 3.70.

Pedro Marquez

thanks but I'm newbie
with examples learn best

Paul Squires

This is code that I have been using inside FireFly to shell and create a .RES resource file. Probably got it from the PB Forum.


'--------------------------------------------------------------------------------------------------
Function ShellConsoleApp( CmdLine As String, _
                          StartDirectory As Asciiz, _
                          Result As String, _
                          ByVal dwMillisecondsWait As Dword _
                          ) As Long
                         
      '**** Set security attributes ****
      Local sa As SECURITY_ATTRIBUTES
      sa.nLength = SizeOf(SECURITY_ATTRIBUTES)
      sa.bInheritHandle = 1 ' TRUE

       '**** Create a new pipe ****
      Local hReadPipe As Dword, hWritePipe As Dword
      If CreatePipe(hReadPipe, hWritePipe, sa, ByVal 0) = 0 Then _
         Function = -2: Exit Function

      '**** Start the program ****
      Local SI As STARTUPINFO, pi As PROCESS_INFORMATION, ExitCode As Long
      SI.cb           = SizeOf(STARTUPINFO)
      SI.dwFlags      = %STARTF_USESHOWWINDOW Or %STARTF_USESTDHANDLES
      SI.wShowWindow  = %SW_HIDE
      SI.hStdOutput   = hWritePipe
      SI.hStdError    = hWritePipe

      If CreateProcess ("", ByVal StrPtr(CmdLine), ByVal 0&, ByVal 0&, 1,  %NORMAL_PRIORITY_CLASS, _  ' // creation flags
         ByVal 0&, StartDirectory, si, pi) = 0 Then Function = -1: Exit Function
      WaitForSingleObject PI.hProcess, dwMillisecondsWait
      GetExitCodeProcess PI.hProcess, ExitCode
      If ExitCode = %STILL_ACTIVE Then TerminateProcess pi.hProcess, 0: Function = -3 Else Function = ExitCode

      '**** Read the pipe anyway ****
      Local BytesRead As Dword, BytesWritten As Dword
      Local chBuf As Asciiz * 1024, chEOF As Asciiz * 12

      chEOF = "End Of Pipe" ' Something unique
      Result = ""
      Do
         If WriteFile (hWritePipe, chEOF, Len(chEOF), BytesWritten, ByVal 0) = 0 Then Function = -4: Exit Do
         If BytesWritten <> Len(chEOF) Then Function = -4: Exit Do
         Do
            If ReadFile(hReadPipe, chBuf, SizeOf(chBuf), BytesRead, ByVal 0) = 0 Then Function = -4: Exit Do
            If BytesRead = 0 Then Function = -4: Exit Do
            Result = Result + Left$(chBuf, BytesRead)
            If Right$(Result, Len(chEOF)) = chEOF Then Result$ = Left$(Result$, Len(Result) - Len(chEOF)): Exit Do
         Loop
         Exit Do
      Loop
      If Len(Result) Then OemToCharBuff ByVal StrPtr(Result), ByVal StrPtr(Result), Len(Result)

      '*** Close process and pipe handles ****
      CloseHandle hReadPipe
      CloseHandle hWritePipe
      CloseHandle pi.hThread
      CloseHandle pi.hProcess
End Function


Paul Squires
PlanetSquires Software

Elias Montoya


Oh, sorry Pedro, i didn't knew you were new. Good thing paul came to the rescue!. :)
Win7, iMac x64 Retina display 5K, i7-5820K 4.4 ghz, 32GB RAM, All updates applied. - Firefly 3.70.

Pedro Marquez

#9
hi, paul
Perfect, this is what I seek, now study the example.

thanks

Pedro Marquez

ShellConsoleApp ("netsh wlan show networks mode=bssid","",a$,5000)

Not read all output,

¿the time can be detected automatic?


Paul Squires

I ran your command on my laptop and it captured everything. Maybe you should try increasing your timeout from 5 seconds (5000) to maybe 30 seconds (30000).
Paul Squires
PlanetSquires Software

Pedro Marquez

ShellConsoleApp ("netsh wlan show networks mode=bssid","",a$,30000)

No problem of time, output is cut in half


Also detected with upx -- help
output is nothing and the process is open

Paul Squires

Does using the "ExecuteW" and dll in Post#1 capture all of the text from your console?

Can you post the code you are using to display the text received from my ShellConsoleApp function? Maybe something there is cutting off your display string?
Paul Squires
PlanetSquires Software

Pedro Marquez

 ShellConsoleApp ("netsh wlan show networks mode=bssid","",a$,5000)
FF_Control_SetText (HWND_FORM1_RichEdit1,a$)

Nothing is showing and is the program.exe is hidden, active process.



ShellConsoleApp ("netsh wlan show networks","",a$,5000)
FF_Control_SetText (HWND_FORM1_RichEdit1,a$)


Sample text 26 networks  of 32