Skip to content
WindowsDeep Dive Published Updated 5 min readViews unavailable

Windows UI Automation Cache Requests: Scope, Snapshots, and Safe Batching

Use UI Automation CacheRequest to batch selected properties without confusing search scope, cache scope, or stale element snapshots.

Windows UI Automation clients often inspect several properties across several controls. Reading each property from a live AutomationElement can require a cross-process call. A CacheRequest lets a client name the properties and control patterns it needs and fetch them as part of retrieving elements, then read those values from a local snapshot. This is a bulk-fetching mechanism, not a promise that a cached element stays synchronized with a changing interface. [1]

Declare the cache before retrieving elements

Build a request by adding the exact properties and control patterns the client will use. A cached control pattern does not automatically include all of that pattern’s properties; add each desired property explicitly. If code later requests a property that was not cached through a cached-only reference, Microsoft documents that as unavailable. Caching too little produces cache misses; caching a whole tree by default can return far more data than the task needs. [1]

The following example follows Microsoft’s managed UI Automation pattern. It locates a list among a window’s direct children, requests the list element and its children, and caches each item’s Name property. The FindFirst scope and the cache request’s TreeScope are separate choices: the first searches the parent window’s children; the second describes which returned element objects receive cached data. [2]

using System;
using System.Windows.Automation;

static void PrintListItems(AutomationElement parent)
{
    var request = new CacheRequest();
    request.Add(AutomationElement.NameProperty);
    request.TreeScope = TreeScope.Element | TreeScope.Children;

    var condition = new PropertyCondition(
        AutomationElement.ControlTypeProperty, ControlType.List);
    AutomationElement list;

    using (request.Activate())
    {
        list = parent.FindFirst(TreeScope.Children, condition);
    }

    if (list is null)
        return;

    foreach (AutomationElement item in list.CachedChildren)
        Console.WriteLine(item.Cached.Name);
}

Microsoft’s API example uses this same scope distinction: when the list is retrieved while the request is active, the cache scope is relative to the list being retrieved, not the parent window on which FindFirst was called. The using block also keeps request activation limited to the element-retrieval call. [2][3]

Setting Controls In the example
FindFirst(TreeScope.Children, ...) Which elements are searched from parent Direct children are searched for the list
request.TreeScope Which retrieved element objects receive cached properties The list and its direct children are cached
request.Add(NameProperty) Which property values are copied into the cache Cached.Name is available for each list item

A cache request is not a search request

The distinction matters most when code starts from the desktop. FindAllBuildCache uses a search scope relative to the element on which it is called. Microsoft recommends TreeScope_Children, not TreeScope_Descendants, when looking for top-level desktop windows; a full desktop subtree search can traverse thousands of items and lead to a stack overflow. That advice concerns the search scope argument, not the separate scope configured inside a cache request. If a client might inspect its own interface, the API guidance also says to make UI Automation calls on a separate thread. [4]

Prefer a known window or focused control as the starting point, a condition that narrows the result, and the smallest useful search and cache scopes. For example, searching the direct children of a known window for a list and caching its immediate item names has a clear boundary. Replacing that with an unfiltered desktop-descendant traversal makes cost and tree shape harder to predict.

Cached values have snapshot semantics

The UI can change after a cache is built. A cached Name, IsEnabled, or pattern property describes the snapshot fetched at that time, not necessarily the current control. In the managed API, AutomationElement.GetUpdatedCache(CacheRequest) creates a new snapshot; it does not rewrite the cache held by existing AutomationElement references. Code that refreshes a snapshot must use the returned element rather than assuming older references changed in place. [5]

For event-driven clients, a cache request can also be supplied when subscribing to an event. The event source element contains the properties and patterns configured for that request when the handler runs. Later edits to the request do not alter a subscription already in place. This is useful for event handlers that need a small, predictable set of values, but it does not remove the need to choose what the handler needs before subscribing. [1]

Common cache mistakes

  1. Caching a pattern but not its properties. Add the exact properties the client will read; adding a control pattern alone is not a request to cache every pattern property. [1]
  2. Treating cache scope as the search scope. FindFirst or FindAll selects matches; CacheRequest.TreeScope controls data cached for returned elements. [2][4]
  3. Assuming a refresh mutates old references. Keep the result of AutomationElement.GetUpdatedCache(CacheRequest) and replace or discard references deliberately. [5]
  4. Walking the entire desktop tree. Start at a known element and keep search scope bounded, especially for top-level windows. [4]
  5. Doing re-entrant UI inspection on the application’s own UI thread. When the client may inspect its own UI, use a separate thread as the API guidance specifies. [4]

Cache requests are most reliable when treated as a small data contract: name the values up front, bind their scope to the element actually being retrieved, and treat each result as a point-in-time view. That keeps performance optimization separate from search logic and makes stale-data behavior explicit.

Related:

Sources:

Comments