.net Windows UI自动化没有';无法识别按钮控件

.net Windows UI自动化没有';无法识别按钮控件,.net,vb.net,ui-automation,.net,Vb.net,Ui Automation,我在尝试通过Windows UI自动化识别通知区域窗口内的按钮控件时遇到问题(类名:ToolbarWindow32): 我通过Windows SDK中部署的Windows UI自动化工具验证了这些“图标”是类型为ControlType.Button的控件,但是当我尝试运行下面的代码时,我得到了一个空引用异常,因为我使用的搜索条件没有得到任何控件 我做错了什么,或者我在Windows UI自动化中发现了某种限制 这就是代码,我将其与WinAPI调用混合使用,只是为了方便帮助者用户使用该方法 Di

我在尝试通过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 SDK中部署的Windows UI自动化工具验证了这些“图标”是ControlType.Button类型的控件

你说得有点对。从技术上讲,它们不在Toolbar Window32中,而是在Shell\u TrayWnd中。我检查了该区域,发现这些按钮实际上位于
工具栏中,因此您需要查找
ControlType.ToolBar
。接下来需要
FindAll
,它将返回满足
属性条件的所有自动元素

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
注意:第一个循环是获取用户提升的通知区域。下一个有趣的循环是获取正在运行的应用程序按钮。。。(代码在WIN7、WIN8和WIN10上运行)

在下面的示例中,我将使用
Shell\u TrayWnd
,它将获得我们需要的东西。然后我遍历并找到我们要查找的
工具栏
,然后循环遍历
FindAll
按钮的控制类型

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?另外,我不知道Windows UI自动化工具是如何工作的,但您是否也与Spy++?Visual Vincent Hi进行了检查,谢谢您的评论。我使用的是Windows 10 x64,是的,我也先用WinSpy++检查了值,但它是一个不推荐使用的工具(微软说的,不是我),似乎它无法通过Toolbar Window32窗口的子窗口进行监视,然后我使用了Visual UI Automation Verify tool(来自Windows SDK)来验证所有这些值,然后在这里询问,然而,我不是控制树和窗口层次结构的专家,所以在检查时可能会出错。在我的Windows 10中,ToolbarWindow32窗口的Windows UI自动化Id为1504。我尝试在ToolbarWindow32窗口上调用EnumChildWindows函数,该函数存储通知图标(是按钮控件),但是EnumChildWindows在ToolbarWindow32窗口内不检索任何子窗口句柄,我只能使用Windows SDK中的可视化UI自动化工具识别那些notifyicon按钮。。。真奇怪,奇怪。。。也许这些图标实际上并不属于任务栏,只是画在屏幕上还是别的什么?(只是在这里做一些推测)——而且,WinSpy++和Spy++不是完全一样的东西。WinSpy++由第三方开发人员制作,Spy++由Microsoft制作。虽然我找不到任何东西说它会被弃用。它仍然是最新的Visual Studio附带的。它确实没有帮助,但我可以(通过Winspector)确认“Shell\u TrayWnd>TrayNotifyWnd>ToolbarWindow32”、“Shell\u TrayWnd>TrayNotifyWnd>SysPager>ToolbarWindow32”和“NotifyIconOverflowWindow”都没有子系统。此外,我的系统托盘图标在Winspector中均不显示为“窗口”。可能相关:有人在一篇文章中向我指出,尽管有一个消息循环,NotifyIcon的行为与标准控件不同。感谢您的回答,我只想指出,提供的解决方案在非英语窗口上不起作用,因为代码中硬编码的英语本地化名称,那么也许更好的解决方案是比较类名(ToolbarWindow32,MSTaskListWClass),这至少对我来说是可行的。