ShellAndWait & Listening to an app?

For now, DoEvents seems to does the trick and there is no discernible slowdown... I hope. Will need to do some more tests to be positive.

Will keep your other solution in mind as well.

Also, would I assume you'd use ConsoleReadOutput to read what Console has outputted as a result of execution? ConsoleRead seems to be for a user input only.

I use this:

Code:
Private Declare Function ReadConsoleOutputCharacter Lib "kernel32" Alias "ReadConsoleOutputCharacterA" (ByVal hConsoleOutput As Long, ByVal lpCharacter As String, ByVal nLength As Long, ByVal CoordXY As Long, lpNumberOfCharsRead As Long) As Long
 
Reading the documentation, it looks like I should first get the buffer's position so I can know what coordinate it is at now and give the correct coordinate for it to read from first column on a given row, correct?

If so, for that, I'd use that fucntion; GetConsoleScreenBufferInfo, right?
 
Yes I believe you are correct, it's been a while since I looked at the deep dark underbelly of the console code (I have it in a class module so I don't have to remember all this junk LOL).
 
Okay, a bit stumped here:

You already gave your ReadConsoleOutputCharacter above, but it had problems because it wasn't consistent with API documentation. For example, you defined this parameter:

Code:
ByVal CoordXY As Long

when the documentation says it should be a COORD data type. I tried a slightly different version of the same call.

Code:
Declare Function ReadConsoleOutputCharacter Lib "kernel32" Alias "ReadConsoleOutputCharacterA" _
   (ByVal hConsoleOutput As Long, _
   ByVal lpCharacter As String, _
   ByVal nLength As Long, _
   dwReadCoord As COORD, _
   lpNumberOfCharsRead As Long) As Long

which matches the documentation better.

Even so, it seems to be failing. There are no DLL error but the comparison fails...

Code:
Private Function ReadOutPut(sExpected As String) As Boolean

Dim sBuffer As String * 2000
Dim cBuffer As Integer
Dim StartPos As COORD
Dim StopLine As Integer
Dim Width As Integer
Dim Length As Integer
Dim i As Integer

'Get the position of next row from the previous cursor's position to read the output
StartPos.x = PrePos.x + 1
StartPos.y = 0

'Get the width of the screen buffer
Width = SrnBuf.dwSize.x

'Get the last complete line
StopLine = CurPos.x - 1

For i = StartPos.x To StopLine
    Length = Length + Width
Next i

'Add the current cursor's line
Length = Length + CurPos.y - 1

i = ReadConsoleOutputCharacter(hConsoleOut, sBuffer, Length, StartPos, cBuffer)
Debug.Print Err.LastDllError

'Check to see if we got what we were expecting
If InStr(sBuffer, sExpected) Then
    ReadOutPut = True
Else
    ReadOutPut = False
End If

End Function

sBuffer returns as empty even though the call was successful. Could I be missing something?
 
Okay, a bit stumped here:

You already gave your ReadConsoleOutputCharacter above, but it had problems because it wasn't consistent with API documentation. For example, you defined this parameter:

Code:
ByVal CoordXY As Long

when the documentation says it should be a COORD data type. I tried a slightly different version of the same call.

Code:
Declare Function ReadConsoleOutputCharacter Lib "kernel32" Alias "ReadConsoleOutputCharacterA" _
   (ByVal hConsoleOutput As Long, _
   ByVal lpCharacter As String, _
   ByVal nLength As Long, _
   dwReadCoord As COORD, _
   lpNumberOfCharsRead As Long) As Long

which matches the documentation better.

Even so, it seems to be failing. There are no DLL error but the comparison fails...

Code:
Private Function ReadOutPut(sExpected As String) As Boolean

Dim sBuffer As String * 2000
Dim cBuffer As Integer
Dim StartPos As COORD
Dim StopLine As Integer
Dim Width As Integer
Dim Length As Integer
Dim i As Integer

'Get the position of next row from the previous cursor's position to read the output
StartPos.x = PrePos.x + 1
StartPos.y = 0

'Get the width of the screen buffer
Width = SrnBuf.dwSize.x

'Get the last complete line
StopLine = CurPos.x - 1

For i = StartPos.x To StopLine
    Length = Length + Width
Next i

'Add the current cursor's line
Length = Length + CurPos.y - 1

i = ReadConsoleOutputCharacter(hConsoleOut, sBuffer, Length, StartPos, cBuffer)
Debug.Print Err.LastDllError

'Check to see if we got what we were expecting
If InStr(sBuffer, sExpected) Then
    ReadOutPut = True
Else
    ReadOutPut = False
End If

End Function

sBuffer returns as empty even though the call was successful. Could I be missing something?

Not sure if you're missing something or if it's just Windows weirdness.

You are right that I declared the Coordinates as a LONG instead of coordinates, I'm not actually using the CMD.exe in my console rather a company developed piece of software that I interact with.

I looked at my code and I'm translating the X Y coordinates using this formula

Code:
sCord = (StartX - 1) Or ((StartY - 1) * &H10000)

Can't remember how I came up with this (or more likely where I found it online) but I know it works as I am able to read the screen.

I also came across this site which has a VB console example which might be helpful.

http://vb.mvps.org/samples/project.asp?id=console
 
DJKarl-

Thanks for the hint- I'll look at translating- it's possible that something may be getting lost in translation, seeing how APIs uses so many types that are essentially identical to Visual Basic.

I'm not sure I understand how you can assign two things to one variable using Or? How does VB choose which one to store in variable?

That said, I understand that everything in a string in VB is treated as a Unicode- is there any particular reason why we shouldn't use Unicode version? All samples I've looked seems to prefer ANSI version?

I already saw that sample you linked (and this website was great, too!), but unfortunately, I couldn't see anything different in his sample and mine that would cause the read to return a zero length string.

Once again, thanks so much for all guidance to the date! :)
 
DJKarl-

Thanks for the hint- I'll look at translating- it's possible that something may be getting lost in translation, seeing how APIs uses so many types that are essentially identical to Visual Basic.

I'm not sure I understand how you can assign two things to one variable using Or? How does VB choose which one to store in variable?

That said, I understand that everything in a string in VB is treated as a Unicode- is there any particular reason why we shouldn't use Unicode version? All samples I've looked seems to prefer ANSI version?

I already saw that sample you linked (and this website was great, too!), but unfortunately, I couldn't see anything different in his sample and mine that would cause the read to return a zero length string.

Once again, thanks so much for all guidance to the date! :)

I started digging through my notes and old websites and came across these lines that explains why it needs to be a long and not a COORD structure.

Code:
' This is a little trick that has to be done in order to be able to pass the X and Y coordinates to ReadConsoleOutputCharacter
' They should really be passsed as a COORD structure, but VB won't allow user defined types to be passed by value...
CaptureStart = ConsoleInfo.srWindow.Left Or (ConsoleInfo.srWindow.Top * &H10000)

one of those nice little nuggets MS decides not to let anyone know about. This came from Experts-exchange

http://www.experts-exchange.com/Programming/Languages/Visual_Basic/VB_Controls/Q_21008783.html
 
All right.

I tried to pass the information using the Long structure as you showed, but still get an empty string. I then tried to set the starting position at 0, 1, and 65536 (&H10000) and telling it to read the whole buffer (80*300), and *still* get a empty string...

Can't help but feeling as if I am missing some basic....

(BTW: Thanks for the link but I don't have an account with them... too bad...)

Edit: Silly me- Forgot to modify the API so it pass by values, not by reference. Even so, the string stays remain, no matter what number I input in.... am really scratching my head....

Edit 2: I don't get it- the function only succeed if I submit a negative number, but if I give in a positive number, the function fails with error 12 or invalid access.
Checking the screen buffer's window size, I get all positive numbers...?
 
Last edited:
All right.

I tried to pass the information using the Long structure as you showed, but still get an empty string. I then tried to set the starting position at 0, 1, and 65536 (&H10000) and telling it to read the whole buffer (80*300), and *still* get a empty string...

Can't help but feeling as if I am missing some basic....

(BTW: Thanks for the link but I don't have an account with them... too bad...)

Edit: Silly me- Forgot to modify the API so it pass by values, not by reference. Even so, the string stays remain, no matter what number I input in.... am really scratching my head....

Hmm the buffer has to be the same length that you want to read in and yours

Code:
Dim sBuffer As String * 2000

is set to accept a maximum of 2000 characters and if you're trying to read in (80*300), that's 24000 characters that's over ten times that amount.

I'd try this maybe

Code:
Dim sBuffer As String

sBuffer = String((80*300),Chr(0))
 
Yay!!!!!


That was the missing piece!


I had long ago dropped the *2000 and set the string as a variable length, but apparently API likes a fixed length, and I didn't know about the String function so I can resize string on fly.


Everything is working now!


You're greatest!!!!
 
It always seems to be that one little detail that's missed, glad you found it and got your project working!
 
Just one more thing...

Do you ever have to deal with output where the cursor position may be different from what is actually outputed?

For example:
PreviousCursorPosition.X=9
CurrentCursorPosition.X=0 'That's okay here.

PreviousCurosrPosition.y=5
CurrentCursorPosition.y= 6

Lines of new output between last input and current read:
Code:
Looking up host "127.0.0.1"
Connecting to 127.0.0.1 port 22
Server version: SSH-2.0-OpenSSH_4.7
We claim version: SSH-2.0-PuTTY_Release_0.60
Using SSH protocol version 2
Doing Diffie-Hellman group exchange
Doing Diffie-Hellman key exchange with hash SHA-256
Host key fingerprint is:
ssh-rsa 2048 4f:76:3e:68:3a:da:f0:10:78:c3:35:59:18:f4:cc:42
Initialised AES-256 SDCTR client->server encryption
Initialised HMAC-SHA1 client->server MAC algorithm
Initialised AES-256 SDCTR server->client encryption
Initialised HMAC-SHA1 server->client MAC algorithm
Reading private key file "C:\testkey2.ppk"
Using username "UserName".
Offered public key
Offer of public key accepted
Authenticating with public key "imported-openssh-key"
Access granted
Opened channel for session
Local port 3306 SOCKS dynamic forwarding
Allocated pty (ospeed 38400bps, ispeed 38400bps)
Started a shell/command
Last login: Mon Dec 17 11:09:08 2007 from localhost
Fanfare!!!
You are successfully logged in to this server!!!
←[?1034h←]0;~
←[32mMyUserName@RemoteHost ←[33m~←[0m
$

The cursor thinks it only has advanced one line when it obivously has progressed much more than just one line. Could it be because it doesn't advance when it's executing a program within cmd.exe?

If this isn't easy to trap, I could just add an optional parameter to my ReadConsole function to tell it to read the whole screen buffer (not elegant and won't work if it will appear multiple times on a buffer, but for this case, it's okay)
 
Last edited:
Honestly didn't have to deal with that much in my app, because it wasn't cmd.exe and was a Menu Driven system all the items were in a fixed and predictable space for me. Reading the whole buffer might be the best option, if your prompt has a unique character you could set up a loop to scan until you reach that character, just kinda brainstorming here, as you're well aware by now getting C / C++ APIs to work with VB/VBA is a delicate process.
 
All right, just wanted to know if this was possible as I want to avoid potential bugs due to a shotgun approach as much as possible, but in this case, it's supposed to occur only once, so I think it will be OK to read the whole buffer.

You know what they say about 90% solution. :)

Once again, thanks so much! You're a *huge* help. :)
 
*bump*

I observe that now and then, the console doesn't always close when I tell it to go away. After those commands are executed:

Code:
'Delete console
CloseHandle hConsoleOut
CloseHandle hConsoleIn
FreeConsole

The expected behavior would be that console windows closes, but this is not always the case. I'm not quite sure why- insights?
 
Another issue is that now and then VBA seems to lose the handles to the console. The handles are declared as private variables, and I am sure I don't write anything to it. Has this behavior been observed before? It seems to happen after a period of inactivity or when something happens to the console, but just exactly what, I've not been able to pinpoint yet.

I wonder if I can just declare handles as a constant, since they seem to be always same...
 

Users who are viewing this thread

Back
Top Bottom