从 Windows 应用程序中的文本框中抓取文本
是否可以从单独的可执行文件中包含的文本框中抓取文本?我有一个带有调试窗口的应用程序。调试窗口生成详细日志。但是,日志永远不会保存在任何地方,只能在应用程序内查看。如果应用程序生成异常,我想给自己发送电子邮件,知道已生成异常,以便我可以跳进去检查。还有一个复制文本框的按钮,所以我正在考虑使用 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 应用程序位于: http://www.mediafire.com/file/172r2xapj7p4f2f/状态模拟器.zip
Is it possible to scrape the text from a textbox that is contained within a separate executable? I have an application that has a debug window. The debug window generates a verbose log. However, the log never is saved anywhere and can only be viewed within the app. If the app generates an exception, I'd like to email myself knowing that an exception has been generated so I can hop in and check things out. There is also a button to copy the textbox so I was thinking of using Spy++ to get the command information. However, I don't know where to go from there. Any pointers are greatly appreciated.
I'd prefer to use C# in .NET, but if I need to use C++, I will.
UPDATE:
Based on the comments, I've tried doing the following:
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
However, I can't seem to find the handle of the textbox control. When I enumerate all of the windows, I only see one TextBox object, but I see the parent multiple times throughout the enumeration. How can I get the pointers to the controls within the window?
UPDATE 2:
I've uploaded a sample Windows app to show the type of app I'm trying to get access to. I'm trying to get the values of both labels in addition to the textbox. The textbox is the most important. The sample Win app is here: http://www.mediafire.com/file/172r2xapj7p4f2f/StatusSimulator.zip
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
文本抓取是通过辅助功能接口完成的。对于托管代码,您可以使用 System.Windows.Automation 命名空间中的类。如果您已经有一个窗口句柄,那么提取文本很简单:(
有点困惑,因为问题被标记为 C# 并且您要求 C# 解决方案,但您的代码示例是用 VB 编写的。)
Text scraping is done by accessibility interfaces. For managed code, you can use the classes in the
System.Windows.Automation
namespace. If you already have a window handle, then extracting the text is simple:(Kind of confused since the question is tagged C# and you ask for a C# solution, but your code sample is in VB.)
当您使用 WM_GETTEXT 消息时,您提供了一个指向接收文本的缓冲区的指针。如果您将此消息发送到另一个进程中的窗口,那么您提供的指针将位于您的进程地址空间中,而不是其他进程中。
我做过类似的事情(在另一个进程中抓取窗口),我所做的有效方法是使用 DLL 注入。基本上,您使用 SetWindowsHook 并在 DLL 内提供回调。然后,操作系统将 DLL 加载到其他进程中,您可以确定何时加载到所需的目标进程中。此时您的代码位于另一个进程中,您可以获取窗口文本。
然后就是将其返回到您的应用程序的问题。我自己用共享内存块来做。也许有一种更简单的方法可以完成这一切,但这在过去对我有用。
When you use the WM_GETTEXT message you provide a pointer to a buffer which receives the text. If you're sending this message to a window in another process then the pointer you provide will be in your process address space not the other process.
I've done something similar to this (scraping windows in another process) and what I did which worked was to use DLL injection. Basically you use SetWindowsHook and provide a callback inside a DLL. The OS then loads the DLL into other processes and you figure out when you've loaded into the desired target process. At that point your code is in that other process and you can get the window text.
Then there's the matter of getting it back to your app. I used a shared memory block to do it myself. Maybe there's a simpler way to do all this, though, but this worked for me in the past.
您对
GetWindowText
的 P/Invoke 定义不正确。使用 P/InvokeString
类型仅编组入,而不编组出,这就是您无法返回任何文本的原因。如果将 lpString 参数更改为 StringBuffer ,它将起作用。请注意,在将 StringBuffer 传递给 GetWindowText 之前,您还需要将其容量设置为 1000,因为它需要将已分配的缓冲区传递给它 - 它不会调整 StringBuffer 本身的大小。
请参阅定义和示例以及 pinvoke.net。
Your P/Invoke definition for
GetWindowText
isn't right. With P/InvokeString
types are only marshalled in, and not out which is why you aren't getting any text back. If you change your lpString parameter to aStringBuffer
it will work.Note you also will need to set the capacity of the StringBuffer to 1000 before passing it to
GetWindowText
because it requires that an allocated buffer be passed to it - it won't size the StringBuffer itself.See the definition and example and pinvoke.net.