Adventures in UI Automation: Scraping the Driver Files dialog

Raymond Chen

Raymond

If you go to Device Manager, then view the Properties of a device node, and then go to the Driver tab and click Driver Details, you get a dialog box with a list of driver files, and when you click each file, you get information about it.

Unfortunately, this dialog does not have a Copy button to copy the information to the clipboard. This means that when somebody on the hardware team asks you for the driver information, you have to manually copy down all the drivers and versions.

And by “you” I mean “me”.

As the joke goes, a programmer is someone who will spend six months writing a computer program to save 45 minutes of work. (Obligatory XKCD.)

I’m a programmer. Let’s start.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Automation;
using System.Runtime.InteropServices;

static class AutomationElementHelpers
{
 public static AutomationElement
 FindChildById(this AutomationElement parent, int id)
 {
  return parent == null ? null :
   parent.FindFirst(
    TreeScope.Children,
    new PropertyCondition(AutomationElement.AutomationIdProperty,
                          id.ToString()));
 }

 public static IEnumerable<AutomationElement>
 EnumChildrenOfControlType(this AutomationElement parent, ControlType type)
 {
  return parent == null ? Enumerable.Empty()
                        : parent.FindAll(TreeScope.Children,
   new PropertyCondition(AutomationElement.ControlTypeProperty,
                         type)).Cast<AutomationElement>();
    }
}

The Find­Child­By­Id method looks for a child item with a specific automation ID.

The Enum­Children­Of­Control­Type returns all the children of an automation element with the specified control type.

The last little helper thingie I need is a p/invoke.

static class Win32
{
 [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
 static extern public System.IntPtr
 FindWindowByName(System.IntPtr MustBeZero, string name);
}

Technically, I didn’t need this helper function; I could have used Automation­Element­Helpers.Find, but going straight for the window by title is faster because avoids the full search of the automation tree.

Now let’s snap some blocks together.

class Program
{
 [System.STAThread]
 public static void Main(string[] args)
 {
  // Find the Driver File Details dialog.
  var dialog = AutomationElement.FromHandle(
   Win32.FindWindowByName(IntPtr.Zero, "Driver File Details"));

  // Find the various pieces of the dialog.
  var list = dialog.FindChildById(228);
  var provider = dialog.FindChildById(229);
  var version = dialog.FindChildById(230);
  var copyright = dialog.FindChildById(231);
  var signer = dialog.FindChildById(232);

  // Enumerate and print the list items
  foreach (AutomationElement item in
          list.EnumChildrenOfControlType(ControlType.DataItem))
  {
   System.Console.WriteLine("Driver: {0}", item.Current.Name);

   var pattern = item.GetCurrentPattern(SelectionItemPattern.Pattern)
                 as SelectionItemPattern;
   pattern.Select();

   System.Console.WriteLine("Provider: {0}", provider.Current.Name);
   System.Console.WriteLine("Version: {0}", version.Current.Name);
   System.Console.WriteLine("Copyright: {0}", copyright.Current.Name);
   System.Console.WriteLine("Signer: {0}", signer.Current.Name);
   System.Console.WriteLine();
  }
 }
}

Okay, let’s see what just happened.

First we find the Driver File Details dialog box with Find­Window. As I noted, we could have used Automation­Element­Helpers.Find, but the Find­Window is much faster because it doesn’t have to search the entire automation tree. (And I got tired of waiting for the tree search each time I wanted to test the program.)

Once we find the dialog, we locate the important pieces of the dialog via their automation ID:

  • The control that has the list of driver files.
  • The controls that report on the properties of the selected driver file.

Note that automation IDs are not contractual; they can change at any time. But then again, automation is not contractual either, because the user interface can change at any time. It’s handy for little tools like this, but you have to be aware that the automation may need to be updated if the user interface changes.

We enumerate all the children of the list which say that they are a data item. This filter may seem unnecessary to the casual observer, but it’s important, because a device with a huge number of driver files—I’m looking at you, the display driver—will probably end up showing a scroll bar, and we don’t want to enumerate that.

For each item, we select it, and then read out the values for the provider, version, copyright, and signer.

And that’s it. A program to extract the contents of the Driver Files dialog.

Raymond Chen
Raymond Chen

Follow Raymond   

0 comments

Comments are closed.