【问题标题】:Windows UI Automation doesn't recognize button controlsWindows UI 自动化无法识别按钮控件
【发布时间】:2017-01-31 10:46:29
【问题描述】:

我在尝试通过 Windows UI 自动化识别通知区域窗口内的按钮控件时遇到问题(类名:ToolbarWindow32 ):

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

我做错了什么,或者我在 Windows UI 自动化中发现了某种限制?

这是代码,我将它与 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.

有什么解决办法吗?

【问题讨论】:

  • 您使用的是什么版本的 Windows?另外,我不知道 Windows UI 自动化工具是如何工作的,但是您是否也检查过 Spy++?
  • @Visual Vincent 嗨,感谢您的评论。我使用的是 Windows 10 x64,是的,我也首先使用 WinSpy++ 检查了值,但它是一个已弃用的工具(由 Microsoft 而非我说),似乎它无法通过 ToolbarWindow32的子项进行监视> 窗口,然后我使用 Visual UI 自动化验证 工具(来自 Windows SDK)来验证它,然后再问这里,但是我不是控制树和窗口层次结构的专家,所以也许我可以检查时做错了什么。 ToolbarWindow32 窗口的 Windows UI 自动化 ID 在我的 Windows 10 中是 1504
  • 我试图在 ToolbarWindow32 窗口上调用 EnumChildWindows 函数,该窗口存储通知图标(是按钮控件),但是 EnumChildWindows 不会在 ToolbarWindow32 窗口中检索任何子窗口句柄,我只能使用 Windows SDK 中的 Visual UI Automation 工具来识别那些通知图标按钮...好奇怪。
  • 奇怪...也许这些图标实际上并不属于任务栏,而只是绘制在屏幕上或其他什么东西上? (只是在这里做一些推测)——为了记录,WinSpy++ 和 Spy++ 不是完全相同的东西。 WinSpy++ 由第三方开发者制作,Spy++ 由微软制作。虽然我找不到任何说它会被弃用的东西。它仍然随最新的 Visual Studio 一起提供。
  • 这确实没有帮助,但我可以确认(通过 Winspector)“Shell_TrayWnd > TrayNotifyWnd > ToolbarWindow32”、“Shell_TrayWnd > TrayNotifyWnd > SysPager > ToolbarWindow32”和“NotifyIconOverflowWindow”都没有在我的系统上拥有孩子.此外,我的系统托盘图标都没有在 Winspector 中显示为“窗口”。可能相关:在 previous question 中向我指出,尽管有消息循环,但 NotifyIcon 的行为不像标准控件。

标签: .net vb.net ui-automation


【解决方案1】:

我通过部署在 Windows SDK 中的 Windows UI 自动化工具验证了这些“图标”是 ControlType.Button 类型的控件

您是正确的有点。从技术上讲,它们不在 ToolbarWindow32 中,而是在 Shell_TrayWnd。我检查了该区域,发现这些按钮实际上在ToolBar 中,因此您需要查找ControlType.ToolBar。接下来你需要FindAll,它将返回所有满足PropertyCondition的AutomationElements ...

注意:第一个循环是获取用户升级通知区域。下一个有趣的循环是获取正在运行的应用程序按钮...(代码在 WIN7、WIN8 和 WIN10 上工作)

在下面的示例中,我使用Shell_TrayWnd,它将得到我们需要的东西。然后我遍历并找到我们所追求的ToolBar,然后循环遍历FindAllButton的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

如果您有任何问题,请告诉我。下图(出于安全原因,我注释掉了一些内容)

【讨论】:

  • 感谢您的回答,只是我想指出,提供的解决方案不适用于非英语 Windows,因为代码中硬编码的英语本地化名称,那么也许更好的解决方案可能是而是比较类名(ToolbarWindow32MSTaskListWClass),这至少对我有用。
猜你喜欢
  • 1970-01-01
  • 2021-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多