您的位置:首页 > 产品设计 > UI/UE

使用IAccessible接口,遍历DirectUI窗口控件的问题?

2013-02-04 20:26 260 查看
前一段时间,做一个程序,需要完成一个小功能,即对鼠标监视,当左键单击某个文件选中时,获得该文件文件名称。

折腾了好久,最终在windowsXP下完美实现了。实现的思路是:

1、下鼠标钩子,获得鼠标左键clickup事件。

2、使用FindPointWindow,获得鼠标位置窗口句柄。

3、使用SendMessage,向该窗口(SysListView32)发送信息,获得选中文件序号及文件名称。

该程序在WindowsXP下运行正常,可以正确取出文件名。在Windows7和Server2008的桌面(也是SysListView32)也工作正常。

但是windows7和Server2008的文件浏览窗口时DirectUI,无法使用SendMessage获得相应信息。

针对DirectUI,改用IAccessible接口遍历并获得指定句柄的窗口内鼠标选中的文件。

程序getCurrentFileName,入口是指定窗口的句柄。

但是很奇怪,该程序如果从程序本身的窗体上获得句柄参数,能正常执行,找到选中文件(如从一个textbox中获得句柄值)。但是如果是用鼠标钩子和FindPointWindow获得窗口句柄值,自动调用getCurrentFileName,就无法找到选中的文件。

通过调试,可以看到AccessibleObjectFromWindow这句在两种情况下执行结果不一样。

但是不知道原因在哪里?不知道大家有没有对IAccessible接口熟悉的,帮忙分析一下?

程序如下:

#Region "使用IAccessible读取被选中的文件名称(通用于SysListView32和DirectUI两种控件)"

''' <summary>

''' 从Windows系统窗口中读取被选中的文件名称,需要考虑两种控件。

''' 第一种是SyslistView32,WindowsXp桌面和文件浏览窗口,Windows7、Server2008的桌面都是这种控件

''' syslistView32控件本身的Role值为33【列表】,每个文件Role值为34【列表项目】,ObjectType为Simple Element

''' 第二种是DirectUI,Windows7、Server2008的文件浏览窗口都是这种控件

''' DirectUI控件本身内部较为复杂,文件浏览窗口处于DirectUI较深的层次,文件浏览窗口Role值为33【列表】,每个文件Role值为34【列表项目】

''' 但是特别注意,这里的文件的ObjectType为Container。

''' 两种控件都可以使用IAccessible自动化接口取访问,并且层层深入的遍历。但是特别注意,IAccessible不能进入ObjectType为Simple Element的层。

''' 也就是说使用IAccessible不能用AccessibleChildren进入SyslistView32的子层取遍历控件,这样会报错。这可能是自己对IAccessible接口工作原理还不是很清楚而导致的。

'''

''' 注:这两种控件可以使用工具AccExplore进行研究和查看。

'''

Private Declare Function AccessibleObjectFromWindow Lib "oleacc" ( _

ByVal Hwnd As Int32, _

ByVal dwId As Int32, _

ByRef riid As Guid, _

<MarshalAs(UnmanagedType.IUnknown)> ByRef ppvObject As Object) As Int32

''' <summary>

''' AccessibleObjectFromWindow用于通过调用IAcessible接口,获得指定句柄为Hwnd的窗口com对象整体,将该对象置于ppvObject中,供用户访问

''' AccessibleObjectFromWindow如果获得了正确的Com对象,则返回值为0。如果返回值不为0,则说明获得Com对象过程中出错。

''' 特别注意,AccessibleObjectFromWindow只能返回Hwnd句柄指向窗口的顶级窗口。这点在Windows 7下使用最为明显。

''' </summary>

Public Declare Function AccessibleChildren Lib "oleacc" ( _

ByVal paccContainer As IAccessible, _

ByVal iChildStart As Integer, _

ByVal cChildren As Integer, _

<[Out]()> ByVal rgvarChildren() As Object, _

ByRef pcObtained As Integer) As UInt32

''' <summary>

''' AccessibleChildren用于获得当前Object即paccContainer的子Object,存放在cChildren集中

''' 公用变量strCurrentFileName用于返回获得的文件名

''' </summary>

Public strCurrentFileName As String

Public Sub getCurrentFileName(ByVal hwndCurrent As Int32)

Try

Dim IACurrent As Accessibility.IAccessible = Nothing

Dim ID As Int32 = 0

Dim IID_IAcce As Guid = New Guid("618736E0-3C3D-11CF-810C-00AA00389B71")

'调用AccessibleObjectFromWindow,获得句柄hwndCurrent指向的鼠标当前窗口所在的顶级窗口,放入IACurrent,供访问者使用

Dim aaVal As Int32 = AccessibleObjectFromWindow(hwndCurrent, ID, IID_IAcce, IACurrent)

'IACurrent = DirectCast(IACurrent.accParent, Accessibility.IAccessible) '不运行这条语句可以根据句柄正确获得child的数量和名称。

Dim _ChildCount As Integer = IACurrent.accChildCount

Dim _Children As Object() = New Object(_ChildCount) {}

Dim _out As Integer

'调用AccessibleChildren函数,获得当前顶级窗口(特别注意不一定是hwndCurrent所指向的窗口)的第一层子项,放入_Children

AccessibleChildren(IACurrent, 0, _ChildCount, _Children, _out)

'对第一层子项进行遍历

For Each _child As Accessibility.IAccessible In _Children

'在遍历过程中,会出现取到空子项的情况,如果取到空子项,下面程序会出错,所以需要将空子项排除

If _child IsNot Nothing Then

'首先判断子项的Role值,33为【列表】,文件为34【列表项目】,我们需要找到33【列表】

Dim _accRole As String = _child.accRole(0) '取当前遍历到的子项的Role

'如果控件是SysListView32,则:

'1、不能使用Accessibility.IAccessible进入SysListView32的子项

'2、可以使用_child.accName(i)、_chile.accState(i)、遍历该"33"列表的子项

'3、可以使用 _child.accSelection返回该该"33"列表中被选中的子项,但是该返回值与所期望的值不同。

If _accRole = "33" And strListViewClass = "SysListView32" Then

'找到列表后,判断当前列表是否有子项被选中。

PDMMainForm.LFind33.Text = "找到列表33,名称为" & _child.accName(0)

'如果有子项被选中, _child.accSelection返回被选中的值(该值一定大于零), 否则返回空

Dim _accSelected As String = _child.accSelection

If _accSelected > 0 Then

Dim i As Integer

i = CInt(_accSelected)

strCurrentFileName = _child.accName(i)

PDMMainForm.Lfindfile.Text = "找到选中文件,名称为" & strCurrentFileName

Exit Sub

End If

End If

'判断当前子项_child是否还有下一层子项(_child.accChildCount>0),如果有,则进行遍历

Dim _accCount As String = _child.accChildCount '取当前遍历到的子项的子项数量

If _child.accChildCount > 0 Then

enumchild(_child, _child.accChildCount)

End If

End If

Next

Catch

'如果没有正确获得被选中的文件名称,则屏蔽try语句,进行调试

End Try

End Sub

Sub enumchild(ByVal objParent As Accessibility.IAccessible, ByVal _ChildCount As Integer)

'遍历二级及二级以下所有子控件,寻找Role为34的子控件

Dim _accChildren As Object() = New Object(_ChildCount) {}

Dim _out As Integer

Try

AccessibleChildren(objParent, 0, _ChildCount, _accChildren, _out)

Catch

End Try

'遍历第n层控件

Dim istep As Integer = 0

Try

'在遍历过程中,如果_accChildren为simple Element,则下面语句会报错,使用try语句避免

For Each _child As Accessibility.IAccessible In _accChildren

istep = 1

'在遍历过程中,会出现取到空子项的情况,如果取到空子项,下面程序会出错,所以需要将空子项排除

If _child IsNot Nothing Then

istep = 2

'首先判断子项的Role值,33为【列表】,文件为34【列表项目】,我们需要找到33【列表】

Dim _accRole As String = _child.accRole(0) '取当前遍历到的子项的Role

istep = 3

If _accRole = "34" Then

PDMMainForm.LFind33.Text = "找到列表项34,名称为" & _child.accName(0)

Dim _accState As Object = _child.accState(0)

Const SYSTEM_MOUSEON As UInt32 = 2 ' 对象被Selection的掩码,参考oleacc.h

Dim iMouseOn As UInt32 = _accState And SYSTEM_MOUSEON

If iMouseOn = 2 Then

strCurrentFileName = _child.accName(0)

PDMMainForm.Lfindfile.Text = "找到选中文件,名称为" & strCurrentFileName

End If

With PDMMainForm.CB33list

.Items.Add(_child.accName(0) & "," & _accState & "," & iMouseOn)

End With

End If

Dim _accCount As String = _child.accChildCount '取当前遍历到的子项的子项数量

If _accCount > 0 Then

enumchild(_child, _accCount)

End If

End If

Next

Catch

End Try

End Sub
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐