1

There is an application that runs pretty much 24/7 on this computer. It is run inside a command prompt Window. I would like to be able to capture all of the text currently displayed in the window.

The application is already running (and for unrelated reasons, can't be launched from within VB), so I can't just redirect the output of the process to save the text.

My current method for capturing the text is with the following code:

SendKeys.SendWait("^(a)")
SendKeys.SendWait("^(a)")
SendKeys.SendWait("{enter}")

Dim CmdText As String = Clipboard.GetText
Clipboard.Clear()

The above code sends a select all command to the window (it sends it twice, otherwise the entire windows text isn't captured). It then hits the enter key to load it into the clipboard. I then save the clipboard contents to a variable. It works well, but the major problem is that it requires the window to be in focus.

Is there anyway to capture the text from the CMD window if it is currently out of focus?

Edit: I think I'm getting close to finding a workaround using sendmessage/postmessage. Here is the current code:

Private Declare Function PostMessage Lib "user32.dll" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As String) As Long

Private Const WM_CHAR As Long = &H102
Private Const VK_CONTROL = &H11
Private Const VK_RETURN = &HD

Public Function GetWindowHandle(ByVal processName As String) As IntPtr
    processName = System.IO.Path.GetFileNameWithoutExtension(processName)
    Dim p() = Process.GetProcessesByName(processName)
    Return If(p.Length > 0, p(0).MainWindowHandle, IntPtr.Zero)
End Function

Private Sub GetText()

Dim h As Long = GetWindowHandle("programname.exe")
SendMessage(h, WM_CHAR, 1, 0) 'suppose to simulate Ctrl + A
SendMessage(h, WM_CHAR, 1, 0) 'suppose to simulate Ctrl + A

PostMessage(h, WM_KEYDOWN, VK_RETURN, 0) 'sends enter key to load text into clipboard

End Sub

The problem is that instead of sending Ctrl + A to the command window, it just sends the text ^A. Any ideas?

user1159415
  • 111
  • 7
  • I'm not sure if it's possible to capture the stdout from a process that was not started with `RedirectStandardOutput = true` *(I find it highly unlikely)*, but check [this question](https://stackoverflow.com/q/2095826/4934172), you might find something useful there. – 41686d6564 stands w. Palestine Apr 01 '18 at 20:09
  • how can an application run in cmd window ?? do u mean it's a console application ? – Software Dev Apr 01 '18 at 20:09
  • @zackraiyan Yes, I mean a console application. – user1159415 Apr 01 '18 at 20:16
  • @AhmedAbdelhameed I don't necessarily need the entire output (in fact I would prefer not getting it). I just want whatever text is currently displayed in the command window. Essentially capturing a moment in time of the console application. – user1159415 Apr 01 '18 at 20:17
  • @user1159415 which is exactly what can be done by capturing the output *(when eligible)*. I'm not aware of any other way, unfortunately. – 41686d6564 stands w. Palestine Apr 01 '18 at 20:27
  • Take a look at [this answer](https://stackoverflow.com/a/36366899/4934172). – 41686d6564 stands w. Palestine Apr 01 '18 at 20:30
  • @AhmedAbdelhameed I've been trying to use sendmessage/postmessage as a bit of a workaround, since they can send commands to windows without focus. If I could use one of these to send the "select all" commands (Ctrl + A), that might solve the problem. I can't seen to get it to send Ctrl + A though. When I use SendMessage(h, WM_CHAR, 1, 0), it ends up just typing ^A into the command window. I think I'm getting closer though. – user1159415 Apr 01 '18 at 20:45
  • You should be able to grab the text using UI Automation. This is the technology used by accessibility tools like screen readers. I have checked and "CMD.exe" exposes a document element that is readable, so I suspect your application would as well. This would need to be verified by you using the [Inspect tool](https://msdn.microsoft.com/en-us/library/windows/desktop/dd318521(v=vs.85).aspx) This technology has a bit of a learning curve, but I am willing to post an example if you want to pursue this option. The big benefit is no clipboard involved. – TnTinMn Apr 02 '18 at 01:37

1 Answers1

2

I've written a library called InputHelper which could come in handy here. It includes different methods of performing input simulation, one being sending it to a specific window.

Download from GitHub:
https://github.com/Visual-Vincent/InputHelper/releases

Its wiki is sadly far from complete, but the library itself includes XML documentation describing every method inside it (which is automatically shown by Visual Studio's IntelliSense when you select a method or member in the members list that pops up while typing).

The library currently consists of four main categories:

  • InputHelper.Hooks: Classes for capturing system-wide (some times referred to as global) mouse and keyboard input. Can be used to make for instance hot keys.

  • InputHelper.Keyboard: Methods for simulating real keyboard input/key strokes.

  • InputHelper.Mouse: Methods for simulating real mouse input.

  • InputHelper.WindowMessages: Methods for simulating mouse and keyboard input at a more virtual level, for instance targeting specific windows. This utilizes window messages (thus SendMessage() and PostMessage()).

The last one mentioned would be the one of your interest. Using InputHelper.WindowMessages.SendKeyPress() you can send a specific key stroke to a window of your choice or, if omitted, the currently active window.

Something like this should work:

Dim hWnd As IntPtr = GetWindowHandle("programname.exe")

'Send CTRL + A twice.
InputHelper.WindowMessages.SendKeyPress(hWnd, Keys.Control Or Keys.A)
InputHelper.WindowMessages.SendKeyPress(hWnd, Keys.Control Or Keys.A)

'Send ENTER.
InputHelper.WindowMessages.SendKeyPress(hWnd, Keys.Enter)

Note that doing Keys.Control Or Keys.A sends the combination CTRL + A, however this works only when using either Keys.Control, Keys.Shift or Keys.Alt (or a combination of them). Using any other keys (for instance Keys.A Or Keys.B or Keys.ControlKey Or Keys.A) won't work.

Visual Vincent
  • 18,045
  • 5
  • 28
  • 75
  • This looks like it would be ideal, but I'm using an older version of Visual Studio (2008), and it doesn't appear to be compatible. – user1159415 Apr 02 '18 at 01:09
  • @user1159415 : Which version of .NET Framework are you targeting? The DLL is compiled for .NET 4.0 but it can be recompiled for older versions. You can also take the source code and put it directly in your project, it's just one file: https://github.com/Visual-Vincent/InputHelper/blob/master/InputHelper%20Library/InputHelper%20Library/InputHelper.vb – Visual Vincent Apr 02 '18 at 08:52
  • I'm using .NET 3.5. I tried adding that VB file manually to my project, but I get over 70 errors in the Error List. There are probably 8 or so different types of errors (Syntax error, initializer expected, optional parameters cannot have structure types, etc). It looks like a bunch of things available in .NET 4.0 aren't available in 3.5 – user1159415 Apr 02 '18 at 17:36
  • @user1159415 : I'll see if I can't make a .NET 3.5 version of it. Give me a little while. – Visual Vincent Apr 02 '18 at 17:48
  • @user1159415 : The reason you got all the errors is because you also use an older compiler than me (Visual Basic 8.0). I use Visual Studio 2010 (thus Visual Basic 10.0), so switching to .NET 3.5 only required me to change one line of code. -- I have released a new version of InputHelper that includes a .NET 3.5 DLL, just visit the _Releases_ page again: https://github.com/Visual-Vincent/InputHelper/releases – Visual Vincent Apr 02 '18 at 18:33
  • Alright, that DLL works! However, it presents the same problem as when I was using SendMessage directly. Instead of actually sending Ctrl + A, it just types out "^A" into the console window. – user1159415 Apr 02 '18 at 18:44
  • @user1159415 : For me pressing `CTRL + A` on the keyboard produces that as well, it doesn't select anything (Windows 7). Does seem strange that it works for you with the normal keyboard, but not when doing it like this. Which OS are you running? – Visual Vincent Apr 02 '18 at 18:53
  • That would explain it. I'm using Windows 10, and the Ctrl + A shortcut isn't available for console applications in Windows 7. – user1159415 Apr 02 '18 at 19:02
  • @user1159415 : I've tested `CTRL + A` with other applications (such as Notepad) and it doesn't work there either. However other combinations (such as `CTRL + C`, `CTRL + V` or `CTRL + Z`) works perfectly, but `CTRL + A` seems to be the only one that doesn't work for some reason. I have checked the window messages received when pressing the keyboard and compared them to that of my library. They're exactly the same, yet it doesn't work. Windows (the OS) must have some other way of dealing with _Select All_ messages. – Visual Vincent Apr 02 '18 at 19:38
  • @user1159415 : I'm going to investigate further. The best you can do in the meantime is to programmatically give the other application focus, then use `InputHelper.Keyboard` instead. After giving the other app focus call `InputHelper.Keyboard.PressKey(Keys.Control Or Keys.A)` twice, then `InputHelper.Keyboard.PressKey(Keys.Enter)`. – Visual Vincent Apr 02 '18 at 19:43
  • Yeah, that's how I am doing it currently. (making sure the window is in focus, and then using SendKeys). Thanks for all the help. I suspect this is a Windows limitation. – user1159415 Apr 02 '18 at 20:02
  • @user1159415 : I don't think it's a limitation, I rather think I've missed sending something to the window. We'll see if I find anything. -- By the way you can P/Invoke [**`SetForegroundWindow()`**](https://msdn.microsoft.com/en-us/library/windows/desktop/ms633539(v=vs.85).aspx) so that you don't have to focus the window manually. – Visual Vincent Apr 02 '18 at 20:07