Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Capturing output stream from console app
#1
Hi Gintaras,
We have a monitoring app that outputs a continuous stream to a cmd console window.
Using Console2

Macro ReadCmdOutput
Code:
Copy      Help
str commandLine = "C:\SampleMonitoringApp.exe"
RunConsole2(commandLine)

I was able to capture some of the output but only when it exits.
But I need to capture it continuously a line at a time. I will process it in real time as the line comes up (parse, trigger or store to file)
Is there an easy way to do this from QM?

Something like streamreader like in this link but in QM
http://stackoverflow.com/questions/28576...out-in-net

Thanks!!!
S
#2
Function RunConsoleCallback
Code:
Copy      Help
;/
function# cbFunc cbParam $cl [$curDir] [flags] ;;flags: 1 show window, 0x200 exclude stdError, 0x400 ANSI, 0x800 UTF-8, 0x1000 UTF-16, 0x4000 64-bit System folder

;Runs a console program. Calls a callback function that receives its output in real time. Or displays in QM output.
;Returns the exit code of the process.
;Error if fails or the file does not exist.

;cbFunc, cbParam - callback function, and some value to pass to it. Must begin with:
;;;function# cbParam $s
;;;;cbParam - cbParam of RunConsoleCallback. Can be declared as TYPE&cbParam if you want to pass address of a variable of type TYPE.
;;;;s - console output line text.
;;;;The return value currently is not used.
;;;If cbFunc is 0, displays console output text in QM output panel.
;cl - program name or full path, optionally followed by command line parameters.
;;;Must be exe, not a document.
;;;Example: "$desktop$\folder\program.exe /a ''c:\new folder\file.txt''".
;;;Program path should be enclosed in quotes if contains spaces.
;;;Expands path even if program path is enclosed in quotes. Example: "''$my qm$\an.exe'' /a".
;curDir - current directory for the program.
;flags:
;;;0-255 - console window show state, like with ShowWindow. If 0 (default), it is hidden.
;;;0x200 - don't get standard error output.
;;;0x400 - console text encoding is current user's defaul ANSI encoding, not OEM.
;;;0x800 - console text encoding is UTF-8, not OEM.
;;;0x1000 - console text encoding is UTF-16, not OEM.
;;;0x4000 - temporarily disable file system redirection from 64-bit system folder (System32) to 32-bit system folder (SysWOW64). Without this flag, starting from QM 2.4.8, this function retries with disabled redirection if file not found.

;REMARKS
;Unlike <help>RunConsole2</help>, allows to capture console output lines immediately, in real time, not when the process ends.
;Expands special folder string in program's path and in curDir, but not in command line arguments.
;While waiting, this thread cannot receive window/dialog messages, COM events, hooks. If need, call this function from separate thread (mac).

;EXAMPLE
;out
;int ec=RunConsoleCallback(&sub.OnConsoleOutput 0 "$my qm$\console2.exe /ab cd")
;out ec
;
;#sub OnConsoleOutput
;function# cbParam $s
;out F"<{s}>"


;create pipe
__Handle hProcess hOutRead hOutWrite
SECURITY_ATTRIBUTES sa.nLength=sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle=1

if(!CreatePipe(&hOutRead &hOutWrite &sa 0)) end "" 16
SetHandleInformation(hOutRead HANDLE_FLAG_INHERIT 0)

;create process
PROCESS_INFORMATION pi
STARTUPINFOW si.cb=sizeof(STARTUPINFOW)
si.dwFlags=STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW
si.hStdOutput=hOutWrite
if(flags&0x200=0) si.hStdError=hOutWrite
si.wShowWindow=flags&255
str s1 s2 s3
if(cl and cl[0]=34) s1.expandpath(cl+1); s1-"''"; else s1.expandpath(cl)
if(!empty(curDir)) s2.expandpath(curDir)

;g1
__DisableFsRedirection dfsr; if(flags&0x4000) dfsr.Disable

if(!CreateProcessW(0 @s1 0 0 1 CREATE_NEW_CONSOLE 0 @s2 &si &pi))
,if(flags&0x4000=0 and GetLastError=ERROR_FILE_NOT_FOUND and _win64) flags|0x4000; goto g1
,end "" 16

hOutWrite.Close
CloseHandle(pi.hThread)
hProcess=pi.hProcess

;read pipe
int n=100000
s1.all(n)
int r
rep
,if(!PeekNamedPipe(hOutRead 0 0 0 &r 0)) break ;;makes easier to end thread etc
,if(r<n) 0.01
,if(!r) continue
,if(!ReadFile(hOutRead s1 n &r 0)) break
,s2.get(s1.lpstr 0 r)
,if s2.len
,,if flags&0x400 ;;ANSI
,,,if(_unicode) s2.ConvertEncoding(0 -1)
,,else if flags&0x800 ;;UTF-8
,,,if(!_unicode) s2.ConvertEncoding(CP_UTF8 0)
,,else if flags&0x1000 ;;UTF-16
,,,s2.ansi
,,else ;;OEM
,,,s2.ConvertEncoding(GetOEMCP -1)
,
,foreach s3 s2
,,if(cbFunc) call(cbFunc cbParam s3)
,,else out s3

int ec
if(!GetExitCodeProcess(hProcess &ec)) ec=-1000

ret ec

console program for testing
Function console2
Code:
Copy      Help
;/exe

ExeConsoleWrite _command
ExeConsoleWrite GetCurDir
ExeConsoleWrite "test error output" 1

int i
for i 1 6
,ExeConsoleWrite F"i={i}"
,0.5

ret 7

;BEGIN PROJECT
;exe_file $my qm$\console2.exe
;flags 70
;END PROJECT
#3
so very amazing! Thanks!
S
#4
See also: Input commands in a exe through cmd.exe and get output in QM
#5
I get the following error
__DisableFsRedirection    =>   unknown identifier.

Is there something else I need to import?
(tried to rename to: _DisableFsRedirection and DisableFsRedirection both give: unknown identifier.)
#6
It is in new QM versions. If you don't have it, remove code that uses __DisableFsRedirection .

Replace
Code:
Copy      Help
;g1
__DisableFsRedirection dfsr; if(flags&0x4000) dfsr.Disable

if(!CreateProcessW(0 @s1 0 0 1 CREATE_NEW_CONSOLE 0 @s2 &si &pi))
,if(flags&0x4000=0 and GetLastError=ERROR_FILE_NOT_FOUND and _win64) flags|0x4000; goto g1
,end "" 16


with
Code:
Copy      Help
if(!CreateProcessW(0 @s1 0 0 1 CREATE_NEW_CONSOLE 0 @s2 &si &pi)) end "" 16
#7
I was running an older version of QM, thank you for this!
The function works but I still can't get the de output streamed into the rich editfield.
It outputs everything after 'console2.exe' is finished. (or it seems that way?)

I am probably using the function and/or callback function wrong.

See code below:

Function curl_output_test2
Code:
Copy      Help
str dd=
;BEGIN DIALOG
;0 "" 0x90C80AC8 0x0 0 0 572 194 "Dialog"
;3 Button 0x54032000 0x0 177 12 132 22 "start"
;5 RichEdit20A 0x54233044 0x200 5 44 562 149 ""
;END DIALOG
;DIALOG EDITOR: "" 0x2040A00 "*" "" "" ""


str controls = "5"
str re5
if(!ShowDialog(dd &sub.DlgProc &controls)) ret


#sub DlgProc
function# hDlg message wParam lParam

sel message
,case WM_INITDIALOG
,,ExeOutputControl id(5 hDlg)
,,int- main_dlg;main_dlg=hDlg
,case WM_DESTROY
,case WM_COMMAND goto messages2
ret
;messages2
sel wParam
,case IDOK
,case IDCANCEL
,case 3
,,int ec=RunConsoleCallback(&sub.OnConsoleOutput 0 "$my qm$\console2.exe /ab cd")
ret 1

#sub OnConsoleOutput
function# cbParam $s
out F"<{s}>"
#8
This function cannot be used in UI thread. As well as many other "wait" functions. As well as most automation scripts. It blocks UI thread and it cannot receive messages and display text etc. Use mac to start other thread and call this function from there.
#9
Thank you!
It works!


Forum Jump:


Users browsing this thread: 2 Guest(s)