简体   繁体   中英

Why is this .net UIAutomation app leaking/pooling?

I've got an app using .net UIAutomation, it eventually runs out of memory and crashes just monitoring windows being shown and closed. Seemed easier to show this in VB than C# but happens the same either way. It appears to be a leak/pool in the underlying proxy objects. Most of the memory is not being shown as in use as .net memory.

Any ideas on how to get this to stop leaking and still monitor StructureChangedEvents?

Imports System.Windows.Automation
Public Class Form1

Delegate Sub AddListCallback(ByVal Text As String)
Dim UIAeventHandler As StructureChangedEventHandler

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    BtnStartStop.Text = "Stop"
    Subscribe()
End Sub

Private Sub BtnStartStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnStartStop.Click
    If "Start" = BtnStartStop.Text Then
        BtnStartStop.Text = "Stop"
        Subscribe()
    Else
        BtnStartStop.Text = "Start"
        Unsubscribe()
        lbEvents.Items.Clear()
        GC.GetTotalMemory(True)
    End If
End Sub

Public Sub Subscribe()
    UIAeventHandler = New StructureChangedEventHandler(AddressOf OnUIAutomationEvent)
    Automation.AddStructureChangedEventHandler(AutomationElement.RootElement, TreeScope.Descendants, UIAeventHandler)
End Sub 

Public Sub Unsubscribe()
    Automation.RemoveStructureChangedEventHandler(AutomationElement.RootElement, UIAeventHandler)
End Sub

''' <summary>
''' AutomationEventHandler delegate.
''' </summary>
''' <param name="src">Object that raised the event.</param>
''' <param name="e">Event arguments.</param>
Private Sub OnUIAutomationEvent(ByVal src As Object, ByVal e As StructureChangedEventArgs)
    ' Make sure the element still exists. Elements such as tooltips can disappear
    ' before the event is processed.  
    If e.StructureChangeType = StructureChangeType.ChildrenInvalidated Then
        Exit Sub
    End If
    Dim sourceElement As AutomationElement
    Try
        sourceElement = DirectCast(src, AutomationElement)
    Catch ex As ElementNotAvailableException
        Exit Sub
    End Try
    ' TODO Handle any other events that have been subscribed to.
    Console.WriteLine( "Element : """ & sourceElement.Current.LocalizedControlType & """ """ & sourceElement.Current.Name _
       & """ Struct Change Type : " & [Enum].GetName(GetType(StructureChangeType), e.StructureChangeType) _
       & " Runtime ID : " & getString(e.GetRuntimeId()))
End Sub 

Private Function getString(ByVal ints As Integer()) As String
    getString = ""
    For Each i As Integer In ints
        getString = getString & " " & i.ToString
    Next
End Function
End Class

I would do something like this when you unsubscribe:

Automation.RemoveAllEventHandlers();
UIAeventHandler = null;

UIAutomation will keep some threads alive as long as you've got an AutomationEventHandler object around. Its pretty much a black box to me, but the above has fixed all issues that I had.

@jaws is correct (there are some additional things to think about), but it is in C#. for VB you need to do the following:

Automation.RemoveStructureChangedEventHandler(AutomationElement.RootElement, UIAeventHandler)
UIAeventHandler = Nothing

Because you do not set the UIAeventHandler to nothing, when you unsubscribe then, you are re-assigning the event handler which is already in memory.

To think about. Notice that you are calling subscribe when the form loads and subscribe/unsubscribe with a button. When the form loads then, the event handler is assigned and should work fine, that is until you hit unsubscribe/subscribe (in that order). When you did not set the UIAeventHandler to Nothing then, you are causing a memory leak because you then set the UIAeventHandler to a NEW instance on subscribe. Therefore, you could also do the following as suggested by @jaws :

Imports System.Windows.Automation
Public Class Form1
    Dim UIAeventHandler as StructureChangedEventHandler

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        BtnStartStop.Text = "Stop"
        Subscribe()
    End Sub

    Public Sub Subscribe()
        UIAeventHandler = New StructureChangedEventHandler(AddressOf OnUIAutomationEvent)
        Automation.AddStructureChangedEventHandler(AutomationElement.RootElement, TreeScope.Descendants, UIAeventHandler)
    End Sub 

    Public Sub Unsubscribe()
        Automation.RemoveStructureChangedEventHandler(AutomationElement.RootElement, UIAeventHandler)
        UIAeventHandler = Nothing   'Here is the Important part!
    End Sub

End Class

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM