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. :)
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
You can teach an example use of the function,
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
Thanks, but do not want to use external dll
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. :)
thanks but I'm newbie
with examples learn best
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
Oh, sorry Pedro, i didn't knew you were new. Good thing paul came to the rescue!. :)
hi, paul
Perfect, this is what I seek, now study the example.
thanks
ShellConsoleApp ("netsh wlan show networks mode=bssid","",a$,5000)
Not read all output,
¿the time can be detected automatic?
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).
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
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?
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
Me doy cuenta que si a.txt es de gran tamaño, el programa queda bloqueado.
I realize that if a.txt is large, the program is blocked.
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
button blocked ----- a.txt 17kb
button Command2 ---- b.txt 1kb
(https://www.planetsquires.com/protect/forum/proxy.php?request=http%3A%2F%2Fi.imgur.com%2F2GW1Kdw.jpg&hash=d2e8b53547960ea6761ff5a52f732a3f4028f50f)
What is strange to me is that the line hangs at this line:
Lo que me parece extraño es que el codigo se detiene en esta linea:
If WriteFile (hWritePipe, chEOF, Len(chEOF), BytesWritten, ByVal 0) = 0 Then Function = -4: Exit Do
Para entonces yo aun no veo ninguna diferencia de importancia en la ejecucion de codigo. Yo diria que algo le esta pasando al handle del pipe, pero no estoy seguro.
By then i still can't find anything different in code execution. I would say something is happening to the pipe handle, but im not sure.
Hi,
Are you sure you are using the right declarations? I have been struggling with this in the past and remember the only way I could get it working was to declare the string as a byte.
Local TB As Byte
Local ToWrite$
ToWrite$="abcdefgh"
For I=1 To Len(ToWrite$)
TB=Asc(Mid$(ToWrite$,I,1))
RetValL=WriteFile(hPipeWrite,TB,1,Written,ByVal 0)
Next I
See: http://www.powerbasic.com/support/pbforums/showthread.php?t=40600 (http://www.powerbasic.com/support/pbforums/showthread.php?t=40600)
I only have to write a small string, so I now write them byte by byte (which is clumsy, but it is not a critical part of my program...).
Maybe this helps.
Wilko
upload the chBuf but same problem
Local chBuf As Asciiz * 2048
master or creator have solution
with 5kb of data stops working.
¿TechSupport can have a solution?
Pedro,
Firefly is probably not the problem. I would recommend creating a minimal Powerbasic-only version. I think you will find the problem still exists. If it turns out that the problem still exists, your best bet is to post your problem on the PB forums. There will be many more people that have experience with Console output there. They are just as eager to help as the forum members here.
David
Yes, I think is best.
Interesting. I wrote a program using the exact code Paul used as reference a few months back. I needed an easy way to call the Android ADB service and make it forward a TCP port over USB for communication with an android app wrote in Java with Eclipse. Unfortunately it wasn't as automated as I wanted since ADB doesn't use any return codes when ran and all you have is the text to go by, but it works...and was wrote in FF/PB.
you can convert ShellConsoleApp to FreeBasic