How to capture visible text from Command Window

A

Avi

Hi,

I need to capture visible text from a command line application for
some automation purpose. I have created functions using WIN32 API
functions GetForegroundWindow, GetWindowText, SendMessage etc.
[ Below is the functions I am using. ]

**Challenge I am facing is - when i try to get text from console
application it gives only the title message, other details are
missing. **

Can someone please help.

Thanks,
Av

[DllImport("user32.dll")]
static extern int GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(int hWnd, StringBuilder text,
int count);

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string
lpWindowName);

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr
hwndChildAfter, string lpszClass, string lpszWindow);

[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, int
wParam, StringBuilder lParam);

[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int msg, int
wParam, int lParam);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetClassName(IntPtr hWnd,
StringBuilder lpClassName, int nMaxCount);

public static string GetWindowClassName(IntPtr hWnd)
{
StringBuilder buffer = new StringBuilder(128);
GetClassName(hWnd, buffer, buffer.Capacity);
return buffer.ToString();
}

[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window,
EnumWindowProc callback, IntPtr i);

public Form1()
{
InitializeComponent();
}


#region GetChildWindows
/// <summary>
/// Returns a list of child windows
/// </summary>
/// <param name="parent">Parent of the windows to return</
param>
/// <returns>List of child windows</returns>
public static List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc
(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr
(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}

/// <summary>
/// Callback method to be used when enumerating windows.
/// </summary>
/// <param name="handle">Handle of the next window</param>
/// <param name="pointer">Pointer to a GCHandle that holds a
reference to the list to fill</param>
/// <returns>True to continue the enumeration, false to bail</
returns>
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target could
not be cast as List<IntPtr>");
}
list.Add(handle);
// You can modify this to check to see if you want to
cancel the operation, then return a null here
return true;
}

/// <summary>
/// Delegate for the EnumChildWindows method
/// </summary>
/// <param name="hWnd">Window handle</param>
/// <param name="parameter">Caller-defined variable; we use it
for a pointer to our list</param>
/// <returns>True to continue enumerating, false to bail.</
returns>
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr
parameter);
#endregion

private const int WM_GETTEXTLENGTH = 0x000E;
private const int WM_GETTEXT = 0x000D;

private void GetActiveWindow()
{
const int nChars = 256;
int handle = 0;
int length = 0;
string text = String.Empty;
StringBuilder Buff = new StringBuilder(nChars);
string sClassName = String.Empty;

handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
this.txtHandle.Text = handle.ToString();
this.txtCapturedText.Text = Buff.ToString();
}
text = this.txtCapturedText.Text + Environment.NewLine;
IntPtr winHwnd = FindWindow(null, Buff.ToString());//,
null);
List<IntPtr> lstWinHdle = GetChildWindows(winHwnd);

this.lstObjects.Items.Clear();
this.lstObjects.Items.Add(GetWindowClassName(winHwnd));
for (int i = 0; i < lstWinHdle.Count; i++ )
{
length = SendMessage(lstWinHdle, WM_GETTEXTLENGTH,
0, 0);
StringBuilder textTemp = new StringBuilder(length);
int hr = SendMessage(lstWinHdle, WM_GETTEXT, length
+1, textTemp);

text = text + textTemp.ToString() +
Environment.NewLine ;

//iGetClsName = GetClassName(lstWinHdle,
sClassName, 255);

this.lstObjects.Items.Add(GetWindowClassName(lstWinHdle
));
}
}
 
J

Jeff Johnson

I need to capture visible text from a command line application for
some automation purpose. I have created functions using WIN32 API
functions GetForegroundWindow, GetWindowText, SendMessage etc.
[ Below is the functions I am using. ]

**Challenge I am facing is - when i try to get text from console
application it gives only the title message, other details are
missing. **

That's because the "text" of a top-level window (like the Command window) is
merely the text in the title bar. So you're getting exactly what you asked
for. I'm not sure what you'll need to do to get into the window's text
buffer, but the route you're currently going is the wrong one. If you do
find the answer, please post a follow-up, even if it's just a link to an
article.
 
P

Peter Duniho

Hi,

I need to capture visible text from a command line application for
some automation purpose. I have created functions using WIN32 API
functions GetForegroundWindow, GetWindowText, SendMessage etc.
[ Below is the functions I am using. ]

**Challenge I am facing is - when i try to get text from console
application it gives only the title message, other details are
missing. **

Since AFAIK Windows doesn't know anything about the text displayed in a
console window, I doubt you can do this without much more complicated
code. You'd need to capture the actual image, and then perform some kind
of optical-character-recognition algorithm on the image to convert it back
to the original text. Of course, there's no shortage of pitfalls in doing
something like that, not the least of which is the problem of dealing with
overlapping windows.

Normally, if you want the text output of a console application, you write
a program that is the one that actually starts the process running the
application, and which redirects the standard output and standard error
streams to capture the text directly as the console application runs. If
you have control over the application being run, this is what you should
do.

Pete
 
A

Avi

Thanks for the replies guys.

The reason i need to capture the console text is - I am trying to
write a .NET plug-in to be used in a automation tool liek QTP to
automate a console based legacy application. My intent there is send
some commands and verify the output received.

As Pete has mentioned, I also feel that OCR is one of the possible
option. I am trying that using MS Document Imaging COM component), but
there are other challenges.

Anyways, If i get a solution, I will post back.

-Av



I need to capture visible text from a command line application for
some automation purpose. I have created functions using WIN32 API
functions GetForegroundWindow, GetWindowText, SendMessage etc.
[ Below is the functions I am using. ]
**Challenge I am facing is - when i try to get text from console
application it gives only the title message, other details are
missing. **

Since AFAIK Windows doesn't know anything about the text displayed in a  
console window, I doubt you can do this without much more complicated  
code.  You'd need to capture the actual image, and then perform some kind  
of optical-character-recognition algorithm on the image to convert it back  
to the original text.  Of course, there's no shortage of pitfalls in doing  
something like that, not the least of which is the problem of dealing with  
overlapping windows.

Normally, if you want the text output of a console application, you write 
a program that is the one that actually starts the process running the  
application, and which redirects the standard output and standard error  
streams to capture the text directly as the console application runs.  If  
you have control over the application being run, this is what you should  
do.

Pete
 
M

marc

Since AFAIK Windows doesn't know anything about the text displayed in a  
console window,

This is wrong. The method has been given many times on Adv. Win32 ng
(news://comp.os.ms-windows.programmer.win32)
 
P

Peter Duniho

This is wrong. The method has been given many times on Adv. Win32 ng
(news://comp.os.ms-windows.programmer.win32)

If it's been given many times, surely you could provide a link to at least
one of the examples.

My guess is that you are misinterpreting what's been written here. But if
Windows really does have a way to retrieve the text from the console
window after the fact, in a documented way (i.e. not just hacking the
console's internal buffer data...obviously the console window itself knows
what text it's displaying and so with enough contortions one can always
get that text), that could be useful information. Why be so coy about
sharing that information with others, if you're so sure it exists?

Pete
 
A

Arne Vajhøj

Peter said:
If it's been given many times, surely you could provide a link to at
least one of the examples.

My guess is that you are misinterpreting what's been written here. But
if Windows really does have a way to retrieve the text from the console
window after the fact, in a documented way (i.e. not just hacking the
console's internal buffer data...obviously the console window itself
knows what text it's displaying and so with enough contortions one can
always get that text), that could be useful information. Why be so coy
about sharing that information with others, if you're so sure it exists?

ReadConsoleOutput/ReadConsoleOutputCharacter

Arne
 
P

Peter Duniho

If it's been given many times, surely you could provide a link to at
least one of the examples.
[...]

ReadConsoleOutput/ReadConsoleOutputCharacter

Sorry Arne, that doesn't really answer the question. There's nothing in
the documentation that suggests to me that from one process, I can get the
handle of a console buffer from another process.

If your intent to is demonstrate that my understanding is wrong (and no
doubt it is), then I'm afraid you'll have to provide more specific details
than just a couple of function names that appear to be intended for use
within a single process for its own console.

Pete
 
A

Arne Vajhøj

Peter said:
This is wrong. The method has been given many times on Adv. Win32 ng
(news://comp.os.ms-windows.programmer.win32)

If it's been given many times, surely you could provide a link to at
least one of the examples.
[...]

ReadConsoleOutput/ReadConsoleOutputCharacter

Sorry Arne, that doesn't really answer the question. There's nothing in
the documentation that suggests to me that from one process, I can get
the handle of a console buffer from another process.

If your intent to is demonstrate that my understanding is wrong (and no
doubt it is), then I'm afraid you'll have to provide more specific
details than just a couple of function names that appear to be intended
for use within a single process for its own console.

It may be a bit confusing because you omitted the text showing
what is being discussed.

But let me repeat.

You wrote:

# Since AFAIK Windows doesn't know anything about the text displayed in
a console window,

If you lookup the API docs for those two functions, then you will
see that Windows do know about the text (and the attributes).

That only proves that it is potentially possibly to get
the information from another process, because the information
exists.

It does not prove that it actually possible or show how to.

But if I were to show that then I would have replied to the
original poster solving his problem.

When I reply to your guesswork, then it is because I am
commenting on that.

Arne
 
P

Peter Duniho

It may be a bit confusing because you omitted the text showing
what is being discussed.

Sorry if I confused you.
But let me repeat.

You wrote:

# Since AFAIK Windows doesn't know anything about the text displayed in
a console window,

I also wrote "obviously the console window itself knows what text it's
displaying". You seem to have overlooked or willfully ignored that part.

In context, it should have been clear that I didn't mean literally the
information isn't anywhere. It's simply that it's not part of the API at
the broader OS level (i.e. it's restricted to the process using it).
[...]
It does not prove that it actually possible or show how to.

But if I were to show that then I would have replied to the
original poster solving his problem.

So in other words, you never really meant to add anything productive to
the discussion. Okay, well...thanks for clearing that up.

I apologize again for not making completely clear to you exactly what it
was I was talking about and for causing you to waste your time writing
comments that weren't applicable to the discussion at hand.

Pete
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top