Skip to content

Commit 9dc36c6

Browse files
committed
Added an opportunity to extract text from a console window.
1 parent 99af510 commit 9dc36c6

File tree

6 files changed

+142
-16
lines changed

6 files changed

+142
-16
lines changed

WindowTextExtractor/Extensions/AutomationElementExtensions.cs

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
42
using System.Text;
3+
using System.Runtime.InteropServices;
4+
using System.ComponentModel;
55
using System.Windows.Automation;
66
using System.Windows.Automation.Text;
77

88
namespace WindowTextExtractor.Extensions
99
{
1010
public static class AutomationElementExtensions
1111
{
12-
public static string GetText(this AutomationElement element)
12+
public static string GetTextFromWindow(this AutomationElement element)
1313
{
1414
object patternObj;
1515
if (element.TryGetCurrentPattern(ValuePattern.Pattern, out patternObj))
@@ -27,5 +27,51 @@ public static string GetText(this AutomationElement element)
2727
return element.Current.Name;
2828
}
2929
}
30+
31+
public static string GetTextFromConsole(this AutomationElement element)
32+
{
33+
try
34+
{
35+
NativeMethods.FreeConsole();
36+
var result = NativeMethods.AttachConsole(element.Current.ProcessId);
37+
if (!result)
38+
{
39+
var error = Marshal.GetLastWin32Error();
40+
throw new Win32Exception(error);
41+
}
42+
var handle = NativeMethods.GetStdHandle(NativeConstants.STD_OUTPUT_HANDLE);
43+
if (handle == IntPtr.Zero)
44+
{
45+
var error = Marshal.GetLastWin32Error();
46+
throw new Win32Exception(error);
47+
}
48+
ConsoleScreenBufferInfo binfo;
49+
result = NativeMethods.GetConsoleScreenBufferInfo(handle, out binfo);
50+
if (!result)
51+
{
52+
var error = Marshal.GetLastWin32Error();
53+
throw new Win32Exception(error);
54+
}
55+
56+
var buffer = new char[binfo.srWindow.Right];
57+
var textBuilder = new StringBuilder();
58+
for (var i = 0; i < binfo.dwSize.Y; i++)
59+
{
60+
uint numberOfCharsRead;
61+
if (NativeMethods.ReadConsoleOutputCharacter(handle, buffer, (uint)buffer.Length, new Coord(0, (short)i), out numberOfCharsRead))
62+
{
63+
textBuilder.AppendLine(new string(buffer));
64+
}
65+
}
66+
67+
var text = textBuilder.ToString().TrimEnd();
68+
return text;
69+
}
70+
catch
71+
{
72+
NativeMethods.FreeConsole();
73+
return null;
74+
}
75+
}
3076
}
3177
}

WindowTextExtractor/Forms/MainForm.cs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,12 @@ public MainForm()
3131
protected override void OnLoad(EventArgs e)
3232
{
3333
base.OnLoad(e);
34-
3534
Application.AddMessageFilter(this);
3635
}
3736

3837
protected override void OnClosed(EventArgs e)
3938
{
4039
base.OnClosed(e);
41-
4240
Application.RemoveMessageFilter(this);
4341
}
4442

@@ -91,13 +89,11 @@ private void menuItemAlwaysOnTop_Click(object sender, EventArgs e)
9189

9290
public bool PreFilterMessage(ref Message m)
9391
{
94-
const int WM_LBUTTONUP = 0x0202;
95-
const int WM_MOUSEMOVE = 0x0200;
9692
if (_isButtonTargetMouseDown)
9793
{
9894
switch (m.Msg)
9995
{
100-
case WM_LBUTTONUP :
96+
case NativeConstants.WM_LBUTTONUP:
10197
{
10298
_isButtonTargetMouseDown = false;
10399
Cursor.Current = _currentCursor;
@@ -107,16 +103,22 @@ public bool PreFilterMessage(ref Message m)
107103
}
108104
} break;
109105

110-
case WM_MOUSEMOVE :
106+
case NativeConstants.WM_MOUSEMOVE:
111107
{
112-
var cursorPosition = System.Windows.Forms.Cursor.Position;
113-
var element = AutomationElement.FromPoint(new System.Windows.Point(cursorPosition.X, cursorPosition.Y));
114-
if (element != null && element.Current.ProcessId != _processId)
108+
try
109+
{
110+
var cursorPosition = System.Windows.Forms.Cursor.Position;
111+
var element = AutomationElement.FromPoint(new System.Windows.Point(cursorPosition.X, cursorPosition.Y));
112+
if (element != null && !element.Current.IsPassword && element.Current.ProcessId != _processId)
113+
{
114+
var text = element.GetTextFromConsole() ?? element.GetTextFromWindow();
115+
txtContent.Text = text;
116+
txtContent.ScrollTextToEnd();
117+
UpdateStatusBar();
118+
}
119+
}
120+
catch
115121
{
116-
var text = element.GetText();
117-
txtContent.Text = text;
118-
txtContent.ScrollTextToEnd();
119-
UpdateStatusBar();
120122
}
121123
} break;
122124
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace WindowTextExtractor
2+
{
3+
static class NativeConstants
4+
{
5+
public const int WM_LBUTTONUP = 0x0202;
6+
public const int WM_MOUSEMOVE = 0x0200;
7+
8+
public const int STD_OUTPUT_HANDLE = -11;
9+
}
10+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Text;
3+
using System.Runtime.InteropServices;
4+
5+
namespace WindowTextExtractor
6+
{
7+
static class NativeMethods
8+
{
9+
[DllImport("kernel32.dll", SetLastError = true)]
10+
public static extern bool AttachConsole(int processID);
11+
12+
[DllImport("kernel32.dll", SetLastError = true)]
13+
public static extern uint GetConsoleOutputCP();
14+
15+
[DllImport("kernel32.dll")]
16+
public static extern bool FreeConsole();
17+
18+
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
19+
public static extern bool ReadConsoleOutputCharacter(IntPtr hConsoleOutput, [Out] char[] lpCharacter, uint nLength, Coord dwReadCoord, out uint lpNumberOfCharsRead);
20+
21+
[DllImport("kernel32.dll", SetLastError = true)]
22+
public static extern bool GetConsoleScreenBufferInfo(IntPtr hConsoleOutput, out ConsoleScreenBufferInfo lpConsoleScreenBufferInfo);
23+
24+
[DllImport("kernel32.dll", SetLastError = true)]
25+
public static extern IntPtr GetStdHandle(int nStdHandle);
26+
}
27+
}

WindowTextExtractor/NativeTypes.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Text;
3+
using System.Runtime.InteropServices;
4+
5+
namespace WindowTextExtractor
6+
{
7+
[StructLayout(LayoutKind.Sequential)]
8+
struct Coord
9+
{
10+
public short X;
11+
public short Y;
12+
13+
public Coord(short x, short y)
14+
{
15+
X = x;
16+
Y = y;
17+
}
18+
};
19+
20+
[StructLayout(LayoutKind.Sequential)]
21+
struct SmallRect
22+
{
23+
public short Left;
24+
public short Top;
25+
public short Right;
26+
public short Bottom;
27+
}
28+
29+
[StructLayout(LayoutKind.Sequential)]
30+
struct ConsoleScreenBufferInfo
31+
{
32+
public Coord dwSize;
33+
public Coord dwCursorPosition;
34+
public ushort wAttributes;
35+
public SmallRect srWindow;
36+
public Coord dwMaximumWindowSize;
37+
}
38+
}

WindowTextExtractor/WindowTextExtractor.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@
5757
<Compile Include="Forms\MainForm.Designer.cs">
5858
<DependentUpon>MainForm.cs</DependentUpon>
5959
</Compile>
60+
<Compile Include="NativeConstants.cs" />
61+
<Compile Include="NativeMethods.cs" />
62+
<Compile Include="NativeTypes.cs" />
6063
<Compile Include="Program.cs" />
6164
<Compile Include="Properties\AssemblyInfo.cs" />
6265
<EmbeddedResource Include="Forms\MainForm.resx">

0 commit comments

Comments
 (0)