views:

28

answers:

1

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
A: 

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
Hmmm, I want to have the listener running for the duration of the app. I've tried stopping and starting several times to see if it releases the memory including the suggestion of clearing the handler so it can be cleaned by the GC. Even though some memory is freed unfortunately it still seems to grow by 1000-2000k each time the event handler is added.
Greg Domjan