CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 8 of 8
  1. #1
    Join Date
    Apr 2014
    Posts
    61

    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

  2. #2
    Join Date
    Oct 2014
    Posts
    7

    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.

  3. #3
    Join Date
    Apr 2014
    Posts
    61

    Re: IAccessible: Code converted from C# not work

    Quote Originally Posted by Accessiblesoft View Post
    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...
    Last edited by FL4SHC0D3R; November 6th, 2014 at 10:50 AM.

  4. #4
    Join Date
    Oct 2014
    Posts
    7

    Re: IAccessible: Code converted from C# not work

    Nice, you are welcome.

  5. #5
    Join Date
    Oct 2014
    Posts
    7

    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

  6. #6
    Join Date
    Apr 2014
    Posts
    61

    Re: IAccessible: Code converted from C# not work

    Quote Originally Posted by Accessiblesoft View Post
    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!, 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
    Last edited by FL4SHC0D3R; November 8th, 2014 at 10:17 AM.

  7. #7
    Join Date
    Oct 2014
    Posts
    7

    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.
    Last edited by Accessiblesoft; November 8th, 2014 at 12:14 PM.

  8. #8
    Join Date
    Apr 2014
    Posts
    61

    Re: IAccessible: Code converted from C# not work

    Quote Originally Posted by Accessiblesoft View Post
    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)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured