vba 使用 FindWindowEx 在应用程序中查找文本框 [英] vba Find Text box in an application using FindWindowEx

查看:117
本文介绍了vba 使用 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 专业版.

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 中的第三个参数上.我使用 Microsoft 的 spy++ 来获取值 WindowsForms10.EDIT.app.0.3cb5890_r6_ad1 因为当我尝试输入 "Edit" 时,它不起作用.

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.

第一种情况很容易通过在字符串末尾附加一个空字符来修复:

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" Then 或通过检查标题).

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆