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

查看:463
本文介绍了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 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屋!

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