Windows UI Automation无法识别按钮控件。

12

我正在尝试通过 Windows UI Automation 识别位于 通知区域 窗口(类名:ToolbarWindow32)内部的按钮控件,并遇到了问题:

enter image description here

我通过部署在 Windows SDK 中的 Windows UI Automation 工具验证了这些“图标”是 ControlType.Button 类型的控件,但是当我尝试运行下面的代码时,我得到了一个空引用异常,因为我使用的搜索条件没有获取到任何控件。

我是做错了什么,还是在 Windows UI Automation 中发现了某种限制?

这是代码,我将其与 WinAPI 调用混合在一起,以便为那些可能更喜欢使用该方法的帮助程序用户提供便利。

Dim tskBarClassName As String = "Shell_TrayWnd"
Dim tskBarHwnd As IntPtr = NativeMethods.FindWindow(tskBarClassName, Nothing)

Dim systrayBarClassName As String = "TrayNotifyWnd"
Dim systrayBarHwnd As IntPtr = NativeMethods.FindWindowEx(tskBarHwnd, IntPtr.Zero, systrayBarClassName, Nothing)

Dim ntfyBarClassName As String = "ToolbarWindow32"
Dim ntfyBarHwnd As IntPtr = NativeMethods.FindWindowEx(systrayBarHwnd, IntPtr.Zero, ntfyBarClassName, Nothing)

Dim window As AutomationElement = AutomationElement.FromHandle(ntfyBarHwnd)
Dim condition As New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button)
Dim button As AutomationElement = window.FindFirst(TreeScope.Descendants, condition)

MsgBox(button.Current.Name) ' Here throws the null-reference exception.

有解决方案吗?


1
你使用的是哪个版本的Windows?此外,我不知道Windows UI自动化工具如何工作,但你是否也用Spy++进行了检查? - Visual Vincent
@Visual Vincent您好,感谢您的评论。我正在使用Windows 10 x64,是的,我首先使用WinSpy++检查了值,但这是一个已弃用的工具(由Microsoft说的,不是我),似乎它无法监视ToolbarWindow32窗口的子项,因此我改用了Visual UI Automation Verify工具(来自Windows SDK)在询问之前进行验证,但是我不是控件树和窗口层次结构的专家,所以可能在检查时做错了什么。在我的Windows 10中,ToolbarWindow32窗口的Windows UI自动化ID为1504 - ElektroStudios
我尝试在存储通知图标(即按钮控件)的ToolbarWindow32窗口上调用EnumChildWindows函数,但是EnumChildWindows无法检索到ToolbarWindow32窗口内的任何子窗口句柄,我只能使用Windows SDK中的Visual UI Automation工具来识别这些通知图标按钮...太奇怪了。 - ElektroStudios
奇怪...也许这些图标实际上并不属于任务栏,只是在屏幕上绘制的或者其他什么?(我只是在猜测)-- 顺便说一下,WinSpy++和Spy++并不完全相同。WinSpy++是由第三方开发者制作的,而Spy++是由微软制作的。虽然我找不到任何关于它将被弃用的信息。它仍然随最新的Visual Studio一起发布。 - Visual Vincent
虽然没有什么帮助,但是我可以通过Winspector确认,我的系统中“Shell_TrayWnd > TrayNotifyWnd > ToolbarWindow32”、“Shell_TrayWnd > TrayNotifyWnd > SysPager > ToolbarWindow32”和“NotifyIconOverflowWindow”都没有子项。此外,在Winspector中,我的系统托盘图标也不显示为“窗口”。可能相关的是:在之前的问题中有人指出,尽管具有消息循环,但NotifyIcon并不像标准控件一样运作。 - Justin Ryan
1个回答

4
我通过部署在Windows SDK中的Windows UI Automation工具进行验证,发现这些“图标”是ControlType.Button类型的控件。 你说的有一定道理,但从技术上讲,它们不在ToolbarWindow32中,而是Shell_TrayWnd。我检查了该区域并发现,这些按钮实际上在ToolBar中,因此需要查找ControlType.ToolBar。接下来,您需要FindAll以返回所有满足PropertyCondition的AutomationElements... 注意:第一个循环是为了获取用户推广的通知区域。下一个循环是获取运行应用程序按钮...(CODE适用于WIN7、WIN8和WIN10)
在我的示例中,我使用Shell_TrayWnd来获取我们所需的内容。然后,我找到我们需要的ToolBar,然后循环查找Button的ControlTypes...
Dim arrText As New List(Of String)
        Dim tskBarClassName As String = "Shell_TrayWnd"
        Dim tskBarHwnd As IntPtr = FindWindow(tskBarClassName, Nothing)
        Dim window As AutomationElement = AutomationElement.FromHandle(tskBarHwnd)
        Dim condition As New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ToolBar)
        Dim elementCollection As AutomationElementCollection = window.FindAll(TreeScope.Descendants, condition)

        'for fun get all we can...
        For Each aE As AutomationElement In elementCollection
            If aE.Current.Name.Equals("User Promoted Notification Area") Then
                For Each ui As AutomationElement In aE.FindAll(TreeScope.Descendants, New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button))
                    arrText.Add("Notification Area - " & Replace(ui.Current.HelpText, Chr(10), " "c)) 'removed line break as when shown it would show some on a new line in messagebox
                Next
            ElseIf aE.Current.Name.Equals("Running applications") Then
                For Each ui As AutomationElement In aE.FindAll(TreeScope.Descendants, New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button))
                    arrText.Add("Toolbar Area - " & Replace(ui.Current.Name, Chr(10), " "c)) 'removed line break as when shown it would show some on a new line in messagebox
                Next
            End If

        Next

If arrText.Count > 0 Then
            MessageBox.Show(String.Join(Environment.NewLine, arrText.ToArray))
        End If

如果您有任何问题,请告知我。下面的图片(因安全原因,某些内容已被我注释掉)。 在此输入图片描述

2
谢谢您的回答,我想指出提供的解决方案在非英语Windows上无法正常工作,因为代码中硬编码了英文本地化名称。也许更好的解决方案是比较类名(ToolbarWindow32MSTaskListWClass),这至少对我有用。 - ElektroStudios

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接