Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIAutomation Memory Issue

I have a simple WPF program that just has a single button with no event handling logic. I then use the UIAutomation framework to click that button many times in a row. Finally, I look at the memory used by the WPF program and it seems to grow and grow.

Anyone know why this is the case and how I can prevent this from happening?

Here is the simple WPF program (nothing in the code behind):

<Window x:Class="SimpleApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Simple Application"
        AutomationProperties.AutomationId="Simple Application"
        Height="350" Width="525">
    <Grid>
        <Button AutomationProperties.AutomationId="button" Height="50" Width="100">Click Me</Button>
    </Grid>
</Window>

Here is the UIAutomation test program:

class Program
{
    static void Main(string[] args)
    {
        string appPath = @"..\..\..\SimpleApplication\bin\Debug\SimpleApplication.exe";
        string winAutoId = "Simple Application";
        string buttonAutoId = "button";

        using (Process process = Process.Start(new ProcessStartInfo(appPath)))
        {
            Thread.Sleep(TimeSpan.FromSeconds(1));

            AutomationElement winElement = AutomationElement.RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, winAutoId));

            for (int i = 0; i < 1001; i++)
            {
                AutomationElement buttonElement = winElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.AutomationIdProperty, buttonAutoId));

                InvokePattern invokePattern = (InvokePattern)buttonElement.GetCurrentPattern(InvokePattern.Pattern);
                invokePattern.Invoke();

                process.Refresh();
                long totalMemory = process.WorkingSet64 + process.PagedMemorySize64;

                if (i % 100 == 0)
                {
                    Console.WriteLine("Memory = {0} MB", ((double)totalMemory) / (1024 * 1024));
                }
            }

            WindowPattern windowPattern = (WindowPattern)winElement.GetCurrentPattern(WindowPattern.Pattern);
            windowPattern.Close();
        }

        Console.WriteLine();
        Console.WriteLine("Press Enter to Continue...");
        Console.ReadLine();
    }
}

Here are the results from the program on my machine:

Memory = 38.20703125 MB
Memory = 42.9296875 MB
Memory = 45.00390625 MB
Memory = 47.04296875 MB
Memory = 51.9296875 MB
Memory = 52.2890625 MB
Memory = 52.41015625 MB
Memory = 55.70703125 MB
Memory = 55.70703125 MB
Memory = 57.21484375 MB
Memory = 59.09375 MB

Looking at it with the .NET Memory Profiler, the new objects that are appearing in the WPF application are from the System.Threading namespace. When I run the WPF program by itself and click the button with the mouse these objects do no appear.

UPDATE:

I tried doing a similar test using Visual Studio's CodedUI, and the same 8 objects appeared to leak in that situation as well. The objects that appear to leak are:

System.Threading.CancellationTokenSource
System.Threading.TimerQueueTimer
System.Threading.SparselyPopulatedArray<CancellationCallbackInfo>[]
System.Threading.Timer
System.Threading.TimerHolder
System.Threading.SparselyPopulatedArray<CancellationCallbackInfo>
System.Threading.SparselyPopulatedArrayFragment<CancellationCallbackInfo>
System.Threading.CancellationCallbackInfo[]

I have also submitted a bug to Microsoft:

http://connect.microsoft.com/VisualStudio/feedback/details/801209/uiautomation-memory-issue

like image 435
user2784456 Avatar asked Sep 16 '13 15:09

user2784456


1 Answers

After talking to Microsoft customer support, we found the answer to the problem. Internally, WPF gives itself three minutes to respond to a UI Automation event. To do this, it starts off a timer. It appears that even if the event is responded to immediately, the timer does not go away until after the three minutes are up.

So, the workaround to the problem is to wait until the timer expires and then do a GC.Collect. Then the memory issue will go away. Not a great fix, but it works for our situation.

like image 110
user2784456 Avatar answered Nov 08 '22 12:11

user2784456