vba使用FindWindowEx在应用程序中的“查找文本”框 [英] vba Find Text box in an application using FindWindowEx
问题描述
我有一个 MS Access
表单,其中包含 Button
来打开应用程序。该应用程序是使用 c#
创建的。我想在窗体中获得 TextBox
,以便使用MS Access项目在其上设置一个值。
I have an MS Access
form that contains Button
to open an application. The application is created using c#
. I want to get the TextBox
in the Form so that I will set a value on it using the MS Access project.
我正在使用以下代码:
hwndParent = FindWindow(vbNullString, "Form1")
If hwndParent <> 0 Then
TextBoxHandle = FindWindowEx(hwndParent, 0&, "WindowsForms10.EDIT.app.0.3cb5890_r6_ad1", vbNullString)
SendMessage TextBoxHandle, WM_SETTEXT, 0&, ByVal strText
End If
以上代码正在我的工作站上运行:Windows 10 Pro。
Above code is working on my workstation: Windows 10 Pro.
当我在Windows 8中打开MS Access时,找不到 TextBox
。
When I open the MS Access in windows 8. it can't find the TextBox
.
TextBoxHandle
在Windows 8中始终返回0。我确定问题出在 FinWindowEx
中的第3个参数上。我使用Microsoft的 spy ++
来获取值 WindowsForms10.EDIT.app.0.3cb5890_r6_ad1
原因,当我尝试仅输入编辑
,它不起作用。
TextBoxHandle
always return 0 in Windows 8. I am sure that the issue is with 3rd parameter in FinWindowEx
. I used spy++
from Microsoft to get the value WindowsForms10.EDIT.app.0.3cb5890_r6_ad1
cause when I try to just enter "Edit"
, it does not work.
推荐答案
编辑:使用来自Hans Passant的动态类名称信息来调整答案。
Adjusted answer using information about dynamic name of class from Hans Passant.
首先,我们将声明WinAPI函数能够迭代通过所有窗口并获得其类名。
First, we're going to declare WinAPI functions to be able to iterate through all windows and get their class name.
Declare PtrSafe Function FindWindowExW Lib "user32" (ByVal hWndParent As LongPtr, Optional ByVal hwndChildAfter As LongPtr, Optional ByVal lpszClass As LongPtr, Optional ByVal lpszWindow As LongPtr) As LongPtr
Declare PtrSafe Function GetClassName Lib "user32" Alias "GetClassNameW" (ByVal hWnd As LongPtr, ByVal lpClassName As LongPtr, ByVal nMaxCount As Long) As Long
然后,我们将声明一个辅助函数以从hWnd获取类名:
Then, we're going to declare a helper function to get the class name from a hWnd:
Public Function GetWindowClass(hWnd As LongPtr) As String
Dim buf(512) As Byte
GetClassName hWnd, varPtr(buf(0)), 255
GetWindowClass = Replace(CStr(buf), Chr(0), "")
End Function
然后,我们将遍历所有顶级窗口,并从与该类名称匹配的那个窗口返回hWnd:
Then, we're going to iterate through all top-level windows, and return the hWnd from the one matching that class name:
Public Function getThehWnd(hWndParent) As LongPtr
Dim hWnd As LongPtr
hWnd = FindWindowExW(hWndParent)
Do While hWnd <> 0
If GetWindowClass(hWnd) Like "WindowsForms10.EDIT.app.0.*" Then
getThehWnd = hWnd
Exit Function
End If
hWnd = FindWindowExW(hWndParent, hWnd)
Loop
End Function
旧答案:
从VBA用字符串调用WinAPI函数时,可能会出错。其中包括传递不以空字符串结尾的字符串,以及传递编码错误的字符串。
There are numerous things that can go wrong when calling WinAPI functions from VBA with strings. These include passing a string that's not terminated by a null string, and passing a string that's in the wrong encoding.
对于第一种情况,您会遇到不稳定的情况。如果该字符串恰好存储在内存中存在很多零的位置,则它可以工作。否则,它将继续从内存中读取字节,并将其附加到字符串中,直到找到两个都为0的字节为止。
For that first case, you get unstable behavior. If the string happens to be stored somewhere where there are a lot of zero's in memory, it works. Else, it continues reading bytes from memory and appending them to the string until it finds two bytes that happen to both be 0.
第一种情况很容易通过附加a来解决。空字符到字符串末尾:
The first case is easily fixed by appending a null character to the end of your string:
TextBoxHandle = FindWindowEx(hwndParent, 0&, "WindowsForms10.EDIT.app.0.3cb5890_r6_ad1" & Chr(0), vbNullString)
请注意,您可能还应该将最后一个参数设为可选。输入 vbNullString
会传递一个指向零长度字符串的指针,该字符串也可能不会由空字符定界,导致WinAPI读取后续字符,直到找到2个空字节为止。将类型设置为 LongPtr
并传递0(默认值)将传递一个实际的空指针,WinAPI会在没有任何字符串输入时期望该空指针。
Note that you should probably also make that last argument optional. Entering vbNullString
there passes a pointer to a zero-length string, that might also not be delimited by a null character, causing WinAPI to read subsequent characters till it finds 2 null bytes. Setting the type to LongPtr
and passing 0 (the default value) passes an actual null pointer, which WinAPI expects when no string gets put in.
第二个代码比较困难。我倾向于使用字节数组来确保VBA不会做奇怪的事情
The second code is more difficult. I tend to use bytearrays to make sure VBA doesn't do weird things
Dim className As Byte(1024)
className = "WindowsForms10.EDIT.app.0.3cb5890_r6_ad1" 'Yes, this is valid, and assigns the first part of the bytearray to a string
FindWindowExW(hwndParent, 0&, VarPtr(className(0)))
FindWindowExW的相应声明:
The corresponding declaration of FindWindowExW:
Declare PtrSafe Function FindWindowExW Lib "user32" (ByVal hWndParent As LongPtr, Optional ByVal hwndChildAfter As LongPtr, Optional ByVal lpszClass As LongPtr, Optional ByVal lpszWindow As String) As LongPtr
要调试问题并确定特定的窗口,我使用以下函数进行迭代通过所有顶部和子窗口,而不是Spy ++。这具有在VBA中运行的优势,因此您可以设置断点和监视,这意味着您可以非常轻松地确定所有打开的窗口的类名和父窗口:
To debug problems and identify specific windows, I use the following function to iterate through all top and child windows, instead of Spy++. This one has the advantage of running in VBA, so you can set breakpoints and watches, which means you can very easily determine the class name and parent window of all open windows:
Public Sub IterateAllWindows(Optional hWnd As LongPtr, Optional EnumLevel = 0)
Dim hwndChild As LongPtr
If hWnd <> 0 Then
Debug.Print String(EnumLevel, "-");
Debug.Print hWnd & ":";
Debug.Print GetWindowName(hWnd);
Debug.Print "(" & GetWindowClass(hWnd) & ")"
hwndChild = FindWindowExW(hWnd)
Do While hwndChild <> 0
IterateAllWindows hwndChild, EnumLevel:=EnumLevel + 1
hwndChild = FindWindowExW(hWnd, hwndChild)
Loop
Else
Dim hWndTopLevel As LongPtr
hWndTopLevel = GetTopWindow
Do While hWndTopLevel <> 0
Debug.Print String(EnumLevel, "-");
Debug.Print hWndTopLevel & ":";
Debug.Print GetWindowName(hWndTopLevel);
Debug.Print "(" & GetWindowClass(hWndTopLevel) & ")"
hwndChild = FindWindowExW(hWndTopLevel)
Do While hwndChild <> 0
IterateAllWindows hwndChild, EnumLevel:=EnumLevel + 1
hwndChild = FindWindowExW(hWndTopLevel, hwndChild)
Loop
hWndTopLevel = GetWindow(hWndTopLevel, 2)
Loop
End If
End Sub
这使用以下2个辅助函数:
This uses the following 2 helper functions:
Public Function GetWindowName(hWnd As LongPtr) As String
Dim buf(512) As Byte
GetWindowText hWnd, varPtr(buf(0)), 255
GetWindowName = Replace(CStr(buf), Chr(0), "")
End Function
Public Function GetWindowClass(hWnd As LongPtr) As String
Dim buf(512) As Byte
GetClassName hWnd, varPtr(buf(0)), 255
GetWindowClass = Replace(CStr(buf), Chr(0), "")
End Function
该子项的相应WinAPI声明:
Corresponding WinAPI declarations for that sub:
Declare PtrSafe Function GetTopWindow Lib "user32" (Optional ByVal hWnd As LongPtr) As LongPtr
Declare PtrSafe Function GetWindow Lib "user32" (ByVal hWnd As LongPtr, ByVal wCmd As Integer) As LongPtr
Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextW" (ByVal hWnd As LongPtr, ByVal lpString As Any, ByVal nMaxCount As Long) As Long
Declare PtrSafe Function FindWindowExW Lib "user32" (ByVal hWndParent As LongPtr, Optional ByVal hwndChildAfter As LongPtr, Optional ByVal lpszClass As LongPtr, Optional ByVal lpszWindow As LongPtr) As LongPtr
Declare PtrSafe Function GetClassName Lib "user32" Alias "GetClassNameW" (ByVal hWnd As LongPtr, ByVal lpClassName As LongPtr, ByVal nMaxCount As Long) As Long
在该类名称上运行手表以运行该功能应有助于您确定它是顶级窗口还是子窗口,如果是子窗口,则它属于哪个类。您还可以修改它以返回独立于嵌套的hWnd(通过使用 If getWindowClass = WindowsForms10.EDIT.app.0.3cb5890_r6_ad1然后
或检查标题)。
Running this function with a watch on that class name should help you identify if it's top-level or a child window, and if it's a child window, which class it belongs to. You can also modify it to return the hWnd independent of nesting (by using an If getWindowClass = "WindowsForms10.EDIT.app.0.3cb5890_r6_ad1" Then
or by checking the title).
这篇关于vba使用FindWindowEx在应用程序中的“查找文本”框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!