我有一个MS Access
表单,其中包含Button
以打开一个应用程序。该应用程序是使用c#
创建的。我想在表单中获取TextBox
,以便使用MS Access项目在其上设置一个值。
我正在使用以下代码:
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。
当我在Windows 8中打开MS Access时,找不到TextBox
。
TextBoxHandle
在Windows 8中始终返回0。我确定问题出在FinWindowEx
中的第3个参数上。当我尝试仅输入spy++
时,我使用了Microsoft的WindowsForms10.EDIT.app.0.3cb5890_r6_ad1
来获取值"Edit"
,这是行不通的。
答案 0 :(得分:1)
编辑:使用来自Hans Passant的班级动态名称信息来调整答案。
首先,我们将声明WinAPI函数,以便能够在所有窗口中进行迭代并获取其类名。
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获取类名:
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:
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函数时,有很多事情可能出错。其中包括传递不以空字符串结尾的字符串,以及传递编码错误的字符串。
对于第一种情况,您将获得不稳定的行为。如果该字符串恰好存储在内存中存在很多零的位置,那么它将起作用。否则,它将继续从内存中读取字节,并将其附加到字符串中,直到找到两个碰巧都为0的字节。
第一种情况很容易解决,只需在字符串末尾附加一个空字符即可。
TextBoxHandle = FindWindowEx(hwndParent, 0&, "WindowsForms10.EDIT.app.0.3cb5890_r6_ad1" & Chr(0), vbNullString)
请注意,您可能还应该将最后一个参数设为可选。输入vbNullString
会传递一个指向零长度字符串的指针,该字符串也可能不会由空字符定界,导致WinAPI读取后续字符,直到找到2个空字节。将类型设置为LongPtr
并传递0(默认值)会传递一个实际的空指针,WinAPI会在没有任何字符串输入时期望该空指针。
第二个代码比较困难。我倾向于使用字节数组来确保VBA不会做奇怪的事情
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的相应声明:
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中运行的优势,因此您可以设置断点和监视,这意味着您可以非常轻松地确定所有打开的窗口的类名和父窗口:
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个辅助函数:
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声明:
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" Then
或通过检查标题)。
答案 1 :(得分:0)
我认为您应该使用Spy在Windows 8上进行与(可能)在Windows 10上进行的相同调查。某些地方必须有所不同,否则您的代码将起作用。
Sidenote(因为它过去对我有影响):确保您运行的Spy版本的“位数”(32位/ 64位)与您感兴趣的应用程序匹配,否则消息记录将不起作用。
此外,为我以前的帖子感到抱歉,这是cr @ p的负担。
编辑啊哈!汉斯在上面说,类名是动态生成的,所以那是。所以现在我们知道了。