IAccessible: Code converted from C# not work
I converted the code following of C#.NET to VB.NET but he not work as the C#, and is same code! but in VB.NET. I already did several changes but until now nothing solved. Someone could help me? Thank in advance.
Here is the code:
PS: Imports Accessibility reference for the project.
Code:
Imports Accessibility
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Linq
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Threading.Tasks
Imports System.Windows.Forms
Public Class Form1
Inherits Form
Private Enum SystemEvents As UInteger
EVENT_OBJECT_FOCUS = &H8005
EVENT_OBJECT_NAMECHANGE = &H800C
EVENT_OBJECT_VALUECHANGE = &H800E
End Enum
Private Const WINEVENT_OUTOFCONTEXT As UInteger = &H0
Shared iAccessible As IAccessible
Shared ChildId As Object
Private Shared Value As String
#Region "APIs"
<DllImport("oleacc.dll")> _
Public Shared Function WindowFromAccessibleObject(pacc As IAccessible, ByRef phwnd As IntPtr) As UInteger
End Function
<DllImport("oleacc.dll")> _
Private Shared Function AccessibleObjectFromEvent(hwnd As IntPtr, dwObjectID As UInteger, dwChildID As UInteger, ByRef ppacc As IAccessible, <MarshalAs(UnmanagedType.Struct)> ByRef pvarChild As Object) As IntPtr
End Function
<DllImport("user32.dll")> _
Private Shared Function SetWinEventHook(eventMin As UInteger, eventMax As UInteger, hmodWinEventProc As IntPtr, lpfnWinEventProc As WinEventDelegate, idProcess As UInteger, idThread As UInteger, _
dwFlags As UInteger) As IntPtr
End Function
Private Delegate Sub WinEventDelegate(hWinEventHook As IntPtr, eventType As UInteger, hwnd As IntPtr, idObject As UInteger, idChild As UInteger, dwEventThread As UInteger, _
dwmsEventTime As UInteger)
#End Region
Private dEvent As WinEventDelegate
Private pHook As IntPtr
Private i As Integer = 0
Public Sub New()
InitializeComponent()
dEvent = AddressOf Me.WinEvent
pHook = SetWinEventHook(CUInt(SystemEvents.EVENT_OBJECT_FOCUS), CUInt(SystemEvents.EVENT_OBJECT_VALUECHANGE), IntPtr.Zero, dEvent, CUInt(0), CUInt(0), _
WINEVENT_OUTOFCONTEXT)
End Sub
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Public Shared Function GetClassName(hWnd As IntPtr, lpClassName As StringBuilder, nMaxCount As Integer) As Integer
End Function
Public Shared Function GetWindowClassName(hWnd As IntPtr) As String
Dim buffer As New StringBuilder(128)
GetClassName(hWnd, buffer, buffer.Capacity)
Return buffer.ToString()
End Function
Public Shared Function GetControlHandlerFromEvent(hWnd As IntPtr, idObject As UInteger, idChild As UInteger) As IntPtr
Dim handler As IntPtr = IntPtr.Zero
handler = AccessibleObjectFromEvent(hWnd, idObject, idChild, iAccessible, ChildId)
Return handler
End Function
<DllImport("user32.dll")> _
Private Shared Function GetForegroundWindow() As IntPtr
End Function
Public Shared Function Gettext() As String
If iAccessible IsNot Nothing AndAlso ChildId IsNot Nothing Then
If GetWindowClassName(GetForegroundWindow()) = "Chrome_WidgetWin_1" Then
Return iAccessible.get_accValue(ChildId)
End If
End If
Return ""
End Function
Private Sub WinEvent(hWinEventHook As IntPtr, eventType As UInteger, hWnd As IntPtr, idObject As UInteger, idChild As UInteger, dwEventThread As UInteger, _
dwmsEventTime As UInteger)
If eventType = CUInt(SystemEvents.EVENT_OBJECT_NAMECHANGE) Then
If Value <> Gettext() AndAlso Gettext() <> "" Then
Value = Gettext()
End If
i += 1
Console.WriteLine("Object" + i)
GetControlHandlerFromEvent(hWnd, idObject, idChild)
Console.WriteLine(Gettext())
TextBox1.Text += Value & vbNewLine
End If
End Sub
End Class
Re: IAccessible: Code converted from C# not work
I saw a couple things wrong in the example.
Here is a working sample for you.
Code:
Imports Accessibility
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Linq
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Threading.Tasks
Imports System.Windows.Forms
Public Class Form1
Inherits Form
Const S_OK As Int32 = 0
Const WINEVENT_OUTOFCONTEXT As Int32 = 0
Const EVENT_MIN As Int32 = 1
Const EVENT_MAX As Int32 = 2147483647
Private Declare Function apiAccessibleObjectFromEvent Lib "oleacc" Alias "AccessibleObjectFromEvent" (ByVal hwnd As Int32, ByVal dwId As Int32, ByVal dwChildId As Int32, ByRef ppacc As Global.Accessibility.IAccessible, ByRef pvarChild As Object) As Int32
Private Declare Function apiSetWinEventHook Lib "user32" Alias "SetWinEventHook" (ByVal eventMin As Int32, ByVal eventMax As Int32, ByVal hmodWinEventProc As Int32, ByVal pfnWinEventProc As EventFuncDelegate, ByVal idProcess As Int32, ByVal idThread As Int32, ByVal dwFlags As Int32) As Int32
Private Declare Function apiUnhookWinEvent Lib "user32" Alias "UnhookWinEvent" (ByVal lHandle As Int32) As Int32
<DllImport("user32.dll")> _
Private Shared Function GetForegroundWindow() As IntPtr
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Public Shared Function GetClassName(hWnd As IntPtr, lpClassName As StringBuilder, nMaxCount As Integer) As Integer
End Function
<System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.FunctionPtr)> Private dEventFunc As EventFuncDelegate
Private Delegate Function EventFuncDelegate(ByVal HookHandle As Int32, ByVal LEvent As Int32, ByVal hwnd As Int32, ByVal idObject As Int32, ByVal idChild As Int32, ByVal idEventThread As Int32, ByVal dwmsEventTime As Int32) As Int32
Private pHook As IntPtr = IntPtr.Zero
Private valu As String = ""
Private Enum SystemEvents As Int32
EVENT_OBJECT_FOCUS = &H8005
EVENT_OBJECT_NAMECHANGE = &H800C
EVENT_OBJECT_VALUECHANGE = &H800E
End Enum
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
dEventFunc = New EventFuncDelegate(AddressOf WinEventFunc)
pHook = apiSetWinEventHook(EVENT_MIN, EVENT_MAX, 0, dEventFunc, 0, 0, WINEVENT_OUTOFCONTEXT)
End Sub
Private Sub Form1_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
apiUnhookWinEvent(pHook) 'important to close the hook, or you could/will get intermittent results when reloading
End Sub
Private Function WinEventFunc(ByVal eHandle As Int32, ByVal lEvent As Int32, ByVal hWnd As Int32, ByVal objId As Int32, ByVal childId As Int32, ByVal eThreadId As Int32, ByVal eTime As Int32) As Int32
'exit here if not the window you want
'If GetWindowClassName(GetForegroundWindow()) <> "Chrome_WidgetWin_1" Then Return 0
If lEvent = SystemEvents.EVENT_OBJECT_NAMECHANGE Then
Dim iacc As Global.Accessibility.IAccessible = Nothing
Dim hAcc As IntPtr = apiAccessibleObjectFromEvent(hWnd, objId, childId, iacc, childId)
If hAcc = S_OK Then
valu = iacc.accName(childId)
'TODO we really should not be doing much extensive code here in the win event function. You can however setup an event with handler to fire from here, that way the function is not delayed, or frozen in an infinite loop
TextBox1.Text = valu
'release resources
iacc = Nothing : childId = Nothing
End If
End If
Return 0
End Function
Public Function GetWindowClassName(hWnd As IntPtr) As String
Dim buffer As New StringBuilder(128)
GetClassName(hWnd, buffer, buffer.Capacity)
Return buffer.ToString()
End Function
End Class
Please find what was wrong with your example and post the reason why here.
Re: IAccessible: Code converted from C# not work
Quote:
Originally Posted by
Accessiblesoft
I saw a couple things wrong in the example.
Here is a working sample for you.
Code:
Imports Accessibility
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Linq
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Threading.Tasks
Imports System.Windows.Forms
Public Class Form1
Inherits Form
Const S_OK As Int32 = 0
Const WINEVENT_OUTOFCONTEXT As Int32 = 0
Const EVENT_MIN As Int32 = 1
Const EVENT_MAX As Int32 = 2147483647
Private Declare Function apiAccessibleObjectFromEvent Lib "oleacc" Alias "AccessibleObjectFromEvent" (ByVal hwnd As Int32, ByVal dwId As Int32, ByVal dwChildId As Int32, ByRef ppacc As Global.Accessibility.IAccessible, ByRef pvarChild As Object) As Int32
Private Declare Function apiSetWinEventHook Lib "user32" Alias "SetWinEventHook" (ByVal eventMin As Int32, ByVal eventMax As Int32, ByVal hmodWinEventProc As Int32, ByVal pfnWinEventProc As EventFuncDelegate, ByVal idProcess As Int32, ByVal idThread As Int32, ByVal dwFlags As Int32) As Int32
Private Declare Function apiUnhookWinEvent Lib "user32" Alias "UnhookWinEvent" (ByVal lHandle As Int32) As Int32
<DllImport("user32.dll")> _
Private Shared Function GetForegroundWindow() As IntPtr
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Public Shared Function GetClassName(hWnd As IntPtr, lpClassName As StringBuilder, nMaxCount As Integer) As Integer
End Function
<System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.FunctionPtr)> Private dEventFunc As EventFuncDelegate
Private Delegate Function EventFuncDelegate(ByVal HookHandle As Int32, ByVal LEvent As Int32, ByVal hwnd As Int32, ByVal idObject As Int32, ByVal idChild As Int32, ByVal idEventThread As Int32, ByVal dwmsEventTime As Int32) As Int32
Private pHook As IntPtr = IntPtr.Zero
Private valu As String = ""
Private Enum SystemEvents As Int32
EVENT_OBJECT_FOCUS = &H8005
EVENT_OBJECT_NAMECHANGE = &H800C
EVENT_OBJECT_VALUECHANGE = &H800E
End Enum
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
dEventFunc = New EventFuncDelegate(AddressOf WinEventFunc)
pHook = apiSetWinEventHook(EVENT_MIN, EVENT_MAX, 0, dEventFunc, 0, 0, WINEVENT_OUTOFCONTEXT)
End Sub
Private Sub Form1_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
apiUnhookWinEvent(pHook) 'important to close the hook, or you could/will get intermittent results when reloading
End Sub
Private Function WinEventFunc(ByVal eHandle As Int32, ByVal lEvent As Int32, ByVal hWnd As Int32, ByVal objId As Int32, ByVal childId As Int32, ByVal eThreadId As Int32, ByVal eTime As Int32) As Int32
'exit here if not the window you want
'If GetWindowClassName(GetForegroundWindow()) <> "Chrome_WidgetWin_1" Then Return 0
If lEvent = SystemEvents.EVENT_OBJECT_NAMECHANGE Then
Dim iacc As Global.Accessibility.IAccessible = Nothing
Dim hAcc As IntPtr = apiAccessibleObjectFromEvent(hWnd, objId, childId, iacc, childId)
If hAcc = S_OK Then
valu = iacc.accName(childId)
'TODO we really should not be doing much extensive code here in the win event function. You can however setup an event with handler to fire from here, that way the function is not delayed, or frozen in an infinite loop
TextBox1.Text = valu
'release resources
iacc = Nothing : childId = Nothing
End If
End If
Return 0
End Function
Public Function GetWindowClassName(hWnd As IntPtr) As String
Dim buffer As New StringBuilder(128)
GetClassName(hWnd, buffer, buffer.Capacity)
Return buffer.ToString()
End Function
End Class
Please find what was wrong with your example and post the reason why here.
Dear friend,
Thank you very much for your answer. It works fine! Thank you!
I am parsing the errors...
Re: IAccessible: Code converted from C# not work
Re: IAccessible: Code converted from C# not work
As a bonus follow up read you can also access these events through UIA(user interface automation).
In this method you subscribe to the events rather than set the hook.
There are differences between the two, so you have to know which one is appropriate for the circumstance.
Here is a quick example of how to DETECT the focus event. I say detect because lengthy code should not be executed within the procedure that could delay or freeze in an infinite loop. Example a system event that fires your code that causes the same system event to fire over again, will loop infinitely or cause memory bugs.
Code:
'import .net reference to automation, ie UIAutomationClient and UIAutomationTypes
Imports System.Windows.Automation
Public Class Form1
Public focHandler As AutomationFocusChangedEventHandler = Nothing
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
SubscribeToFocusChange()
End Sub
Private Sub Form1_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
UnsubscribeFocusChange()
End Sub
Public Sub SubscribeToFocusChange()
Try
focHandler = New AutomationFocusChangedEventHandler(AddressOf OnFocusChanged)
Automation.AddAutomationFocusChangedEventHandler(focHandler)
Catch ex As Exception
End Try
End Sub
Public Sub UnsubscribeFocusChange()
Try
Automation.RemoveAutomationFocusChangedEventHandler(focHandler)
Catch ex As Exception
End Try
End Sub
Private Sub OnFocusChanged(ByVal src As Object, ByVal e As AutomationFocusChangedEventArgs)
'detect focus event here
Beep()
End Sub
End Class
Re: IAccessible: Code converted from C# not work
Quote:
Originally Posted by
Accessiblesoft
As a bonus follow up read you can also access these events through UIA(user interface automation).
In this method you subscribe to the events rather than set the hook.
There are differences between the two, so you have to know which one is appropriate for the circumstance.
Here is a quick example of how to DETECT the focus event. I say detect because lengthy code should not be executed within the procedure that could delay or freeze in an infinite loop. Example a system event that fires your code that causes the same system event to fire over again, will loop infinitely or cause memory bugs.
Code:
'import .net reference to automation, ie UIAutomationClient and UIAutomationTypes
Imports System.Windows.Automation
Public Class Form1
Public focHandler As AutomationFocusChangedEventHandler = Nothing
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
SubscribeToFocusChange()
End Sub
Private Sub Form1_FormClosed(sender As Object, e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
UnsubscribeFocusChange()
End Sub
Public Sub SubscribeToFocusChange()
Try
focHandler = New AutomationFocusChangedEventHandler(AddressOf OnFocusChanged)
Automation.AddAutomationFocusChangedEventHandler(focHandler)
Catch ex As Exception
End Try
End Sub
Public Sub UnsubscribeFocusChange()
Try
Automation.RemoveAutomationFocusChangedEventHandler(focHandler)
Catch ex As Exception
End Try
End Sub
Private Sub OnFocusChanged(ByVal src As Object, ByVal e As AutomationFocusChangedEventArgs)
'detect focus event here
Beep()
End Sub
End Class
Thank you very much @Accessiblesoft!:thumb:, I will see example above, about how works using IUIAutomation interface.;)
I have a small example that use IUIAutomation interface for retrieve infos from elements window, these example retrieve the active url from Google Chrome. See:
Code:
Imports System.Drawing
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
Imports System.Collections.Generic
Imports System.Data
Imports System.Diagnostics
Imports System.Windows.Automation
Public Class Form1
Public Shared Function GetChromeUrl(process As Process) As String
Dim out_url As String = Nothing
If process Is Nothing Then
out_url = Nothing
ElseIf process.MainWindowHandle = IntPtr.Zero Then
out_url = Nothing
Else
Dim element As AutomationElement = AutomationElement.FromHandle(process.MainWindowHandle)
If element Is Nothing Then
Return Nothing
End If
Dim conditions As Condition = New AndCondition(New PropertyCondition(AutomationElement.ProcessIdProperty, process.Id), New PropertyCondition(AutomationElement.IsControlElementProperty, True), New PropertyCondition(AutomationElement.IsContentElementProperty, True), New PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit))
Dim elementx As AutomationElement = element.FindFirst(TreeScope.Descendants, conditions)
out_url = TryCast(DirectCast(elementx.GetCurrentPattern(ValuePattern.Pattern), ValuePattern).Current.Value, String)
End If
Return out_url
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
For Each process__1 As Process In Process.GetProcessesByName("chrome")
Dim url As String = GetChromeUrl(process__1)
If url Is Nothing Then
Continue For
End If
MessageBox.Show(url)
Next
End Sub
End Class
Re: IAccessible: Code converted from C# not work
Very good. Not bad, but sometimes you will want to check if the property is available first. In some cases it does not matter, while in a broader application it will.
Here is a function to retrieve an element value if it has one exposed:
Code:
Public Function GetUIAValue(ByVal el As AutomationElement) As String
If el Is Nothing Then Return ""
Dim val As String = ""
Try
Dim ob As Object = Nothing
ob = el.GetCurrentPropertyValue(AutomationElement.IsValuePatternAvailableProperty, True)
If ob IsNot AutomationElement.NotSupported Then
Dim vp As Object = Nothing
Try
el.TryGetCurrentPattern(ValuePattern.Pattern, vp)
If vp IsNot Nothing Then val = TryCast(vp, ValuePattern).Current.Value
Catch ex As Exception
End Try
End If
ob = Nothing
Catch ex As Exception
End Try
Return val
End Function
Tip: Some elements may be "custom type" and return "not supported", and in those cases you need to bypass the check of availability, and just check it with an error catch ready in case it throws an error.
Re: IAccessible: Code converted from C# not work
Quote:
Originally Posted by
Accessiblesoft
Very good. Not bad, but sometimes you will want to check if the property is available first. In some cases it does not matter, while in a broader application it will.
Here is a function to retrieve an element value if it has one exposed:
Code:
Public Function GetUIAValue(ByVal el As AutomationElement) As String
If el Is Nothing Then Return ""
Dim val As String = ""
Try
Dim ob As Object = Nothing
ob = el.GetCurrentPropertyValue(AutomationElement.IsValuePatternAvailableProperty, True)
If ob IsNot AutomationElement.NotSupported Then
Dim vp As Object = Nothing
Try
el.TryGetCurrentPattern(ValuePattern.Pattern, vp)
If vp IsNot Nothing Then val = TryCast(vp, ValuePattern).Current.Value
Catch ex As Exception
End Try
End If
ob = Nothing
Catch ex As Exception
End Try
Return val
End Function
Tip: Some elements may be "custom type" and return "not supported", and in those cases you need to bypass the check of availability, and just check it with an error catch ready in case it throws an error.
@Accessiblesoft,
very useful also this function above!, changing access modifier for Shared, I adjusted she to my previous example and worked very well!
See:
Code:
out_url = GetUIAValue(elementx)