从Windows应用程序中的文本框中截取文本

时间:2011-12-04 09:59:51

标签: c# c++ winforms winapi screen-scraping

是否可以从单独的可执行文件中包含的文本框中删除文本?我有一个具有调试窗口的应用程序。调试窗口生成详细日志。但是,日志永远不会保存在任何地方,只能在应用程序中查看。如果应用程序生成异常,我想通过电子邮件发送自己知道已经生成异常,因此我可以跳进去检查一下。还有一个用于复制文本框的按钮,因此我正在考虑使用Spy ++来获取命令信息。但是,我不知道从那里去哪里。任何指针都非常感谢。

我更喜欢在.NET中使用C#,但如果我需要使用C ++,我会的。

更新

根据评论,我尝试了以下操作:

Private Declare Function GETWINDOWTEXT Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Integer, ByVal lpString As String, ByVal cch As Integer) As Integer
Private Declare Function SetForegroundWindow Lib "user32.dll" (ByVal hwnd As Integer) As Integer
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindow As String) As IntPtr
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Integer, ByVal hWnd2 As Integer, ByVal lpsz1 As String, ByVal lpsz2 As String) As Integer
Private Declare Ansi Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As String) As Integer
Private Const WM_GETTEXT As Short = &HDS
Private Const WM_GETTEXTLENGTH As Short = &HES

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    Dim hwnd As Integer = FindWindowEx(0, 0, "MyAppForm", "Hello World")

    If Not hwnd = 0 Then
        SetForegroundWindow(hwnd)

        'Dim LabelEx As Integer = FindWindowEx()
        Dim TextBoxEx As Integer = FindWindowEx(hwnd, 0, "MyAppTextBox", vbNullString)
        Dim txtLength As Long = SendMessage(TextBoxEx, WM_GETTEXTLENGTH, CInt(0), CInt(0)) + 1
        Dim txtBuff As String = Space(txtLength)
        Dim txtValue As Long = SendMessage(TextBoxEx, WM_GETTEXT, txtLength, txtBuff)

        MsgBox(txtBuff)
    End If
End Sub

但是,我似乎无法找到文本框控件的句柄。当我枚举所有窗口时,我只看到一个TextBox对象,但我在整个枚举中多次看到父窗口。如何获取窗口中控件的指针?

更新2:

我上传了一个示例Windows应用,以显示我正在尝试访问的应用类型。除了文本框之外,我还试图获取两个标签的值。文本框是最重要的。 Win app示例位于:http://www.mediafire.com/file/172r2xapj7p4f2f/StatusSimulator.zip

3 个答案:

答案 0 :(得分:3)

文本抓取由辅助功能接口完成。对于托管代码,您可以使用System.Windows.Automation命名空间中的类。如果您已有窗口句柄,则提取文本很简单:

AutomationElement.FromHandle(hwnd)
                 .GetCurrentPropertyValue(ValuePattern.ValueProperty) as string;

(有点困惑,因为问题被标记为C#并且您要求使用C#解决方案,但您的代码示例是在VB中。)

答案 1 :(得分:2)

当您使用WM_GETTEXT消息时,您提供指向接收文本的缓冲区的指针。如果您将此消息发送到另一个进程中的窗口,则您提供的指针将位于进程地址空间中,而不是其他进程。

我做了类似的事情(在另一个进程中抓窗),我做的工作就是使用DLL注入。基本上你使用SetWindowsHook并在DLL中提供回调。操作系统然后将DLL加载到其他进程中,并确定何时加载到所需的目标进程。此时,您的代码位于其他进程中,您可以获取窗口文本。

然后就是回到你的应用程序的问题。我使用共享内存块自己做。也许有一种更简单的方法可以做到这一切,但这在过去对我有用。

答案 2 :(得分:1)

GetWindowText的P / Invoke定义不正确。使用P / Invoke String类型只会编组,而不是为什么你没有收到任何文本。如果将lpString参数更改为StringBuffer,它将起作用。

请注意,在将StringBuffer传递给GetWindowText之前,还需要将其设置为1000,因为它需要将已分配的缓冲区传递给它 - 它不会调整StringBuffer本身的大小。

请参阅定义和示例以及pinvoke.net

<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function GetWindowText(ByVal hwnd As IntPtr, ByVal lpString As StringBuilder, ByVal cch As Integer) As Integer
End Function

Private Declare Function SetForegroundWindow Lib "user32.dll" (ByVal hwnd As Integer) 

As Integer
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindow As String) As IntPtr
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Integer, ByVal hWnd2 As Integer, ByVal lpsz1 As String, ByVal lpsz2 As String) As Integer
Private Declare Ansi Function SendMessage Lib "user32.dll" Alias "SendMessageA" (ByVal hwnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As String) As Integer
Private Const WM_GETTEXT As Short = &HDS
Private Const WM_GETTEXTLENGTH As Short = &HES

Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    Dim hwnd As Integer = FindWindowEx(0, 0, "MyAppForm", "Hello World")

    If Not hwnd = 0 Then
        SetForegroundWindow(hwnd)

        'Dim LabelEx As Integer = FindWindowEx()
        Dim TextBoxEx As Integer = FindWindowEx(hwnd, 0, "MyAppTextBox", vbNullString)
        Dim txtLength As Long = SendMessage(TextBoxEx, WM_GETTEXTLENGTH, CInt(0), CInt(0)) + 1
        Dim txtBuff As New System.Text.StringBuilder(txtLength + 1)
        GetWindowText(hWnd, txtBuff , txtBuff .Capacity)
        Dim txtValue As Long = SendMessage(TextBoxEx, WM_GETTEXT, txtLength, txtBuff)
        MsgBox(txtBuff.ToString())
    End If
End Sub