Click here to Skip to main content
13,086,019 members (83,879 online)
Click here to Skip to main content
Add your own
alternative version


55 bookmarked
Posted 11 Aug 2009

UI Automation Using Microsoft Active Accessibility (MSAA)

, 11 Aug 2009
Rate this:
Please Sign up or sign in to vote.
This article describes a new way to automate window applications based on MSAA which otherwise is not possible using any other technique.


Microsoft Active Accessibility is Microsoft’s User Interface technology. All window controls which are based on this technology can expose information about the UI elements to the outside world. It is a COM based technology and provides the IAccessible interface. Controls implementing this interface expose certain methods which provide information about UI elements like location, type, state, name, value, role, and the default action which can be invoked. We use this technology to write automation libraries for MS Word 2007, while the UI is completely based on MSAA.

I would like to share the same experience on this forum as it has given us very promising results.


We started developing a Windows UI automation library based on Microsoft UIA. We are using this library as a platform to write automated functional test cases for Windows clients. As this library is based on Microsoft UIA, it works well with Win32 and WinForms based applications, but it doesn’t work with Windows applications based on MSAA (e.g., MS Office 2007). That’s where we extended our UI automation library for MSAA support and we successfully achieved our mission.


We followed the approach as demonstrated in the diagram below:


  • Win32 API consumes MSAA services from the Windows OS.
  • MSAA Layer is a core automation module. It consumes MSAA services from the Win32 API and provides services to search UI elements on the screen and to get the IAccessible interface as a COM object for that UI element. This layer treats each UI element as an AccessibleUIItem. It can be a window, button, textbox, or any control which implements the IAccessible interface. It provides an efficient searching mechanism to search through an accessible tree.
  • UI Manager defines the object model for the application’s UI which we want to automate. It extends the base AssessibleUIItem provided by the MSAA layer. We will see an example of the UI object model designed for MS Office 2007 later in this article.

Now, let's have a look at each layer in detail. A code walkthrough for each layer would be my focus here.

Win32 API

We have developed the automation library in .NET so we used PInvoke to fetch the services of the Win32 API. Following is the set of Win32 APIs we have used:

[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("user32.dll", EntryPoint = "FindWindow", 
           SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr FindWindowByClass(string lpClassName, IntPtr zero);

[DllImport("user32.dll", EntryPoint = "FindWindow", 
      SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr FindWindowByCaption(IntPtr zero, string lpWindowName);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool EnumChildWindows(IntPtr hWndParent, 
              EnumWindowsProc lpEnumFunc, int lParam);

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int GetWindowText(IntPtr hwnd, 
                         StringBuilder lpString, int cch);

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int GetWindowTextLength(IntPtr hwnd);

[DllImport("user32.dll", SetLastError = true)]
public static extern bool BringWindowToTop(IntPtr hwnd);

public static extern uint GetRoleText(uint dwRole, 
       [Out] StringBuilder lpszRole, uint cchRoleMax);

public static extern uint GetStateText(uint dwStateBit, 
       [Out] StringBuilder lpszStateBit, uint cchStateBitMax);

public static extern uint WindowFromAccessibleObject(IAccessible pacc, 
                                                     ref IntPtr phwnd);

[DllImport("oleacc.dll", PreserveSig = false)]
[return: MarshalAs(UnmanagedType.Interface)]
public static extern object AccessibleObjectFromWindow(int hwnd, 
                            int dwId, ref Guid riid);

public static extern int AccessibleObjectFromWindow(
     IntPtr hwnd,
     uint id,
     ref Guid iid,
     [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppvObject);   

public static extern int AccessibleChildren(IAccessible paccContainer, 
                         int iChildStart, int cChildren,
                         [Out()] [MarshalAs(UnmanagedType.LPArray, 
                         SizeParamIndex = 4)] object[] rgvarChildren, 
                         ref int pcObtained);

public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

MSAA Layer

This layer provides the abstraction over the Win32 API. Its main job is to search the accessible UI item from the whole Accessibility Tree whose root node represents Desktop. After the item is found, it fetches all its properties and provides the MSAAUIItem object. The search mechanism is very optimized. We can ignore the invisible UI items while searching.

"There is a tool available from Microsoft called Accessibility Explorer (AccExplorer32.exe). Using this tool we can easily traverse through all Accessibility Trees."

MSAAUIItme exposes the following UI item properties:

  • Name
  • Role
  • State
  • Location
  • Value
  • Handle
  • IsEnabled
  • DefaultAction


How to Search the UI Item

It is a two step process.


  • Step 1: Get the accessible object of the top application window. For example, the "MS Word 2007" window.
  • public static IAccessible GetTopWindowAccessibleObject(Regex windowName)
        foreach (IAccessible accWindowObject in GetTopWindowAccessibleList())
                string accWindowName = accWindowObject.get_accName(0);
                if (!string.IsNullOrEmpty(accWindowName))
                    if (windowName.Match(accWindowObject.get_accName(0)).Success)
                        return accWindowObject;
            catch (Exception ex)
        return default(IAccessible);
  • Step 2: Use the top window accessible object as the parent object and search for the child object under that. E.g., "Ribbon" is a child object of the "MS Word 2007" window.
  • public static IAccessible GetObjectByName(IAccessible objParent, 
                  Regex objName, bool ignoreInvisible)
        IAccessible objToReturn = default(IAccessible);
        if (objParent != null)
            IAccessible[] children = GetAccessibleChildren(objParent);
            foreach (IAccessible child in children)
                string childName = null;
                string childState = string.Empty;
                    childName = child.get_accName(0);
                    childState = 
                catch (Exception)
                if (ignoreInvisible)
                    if (childName != null 
                        && objName.Match(childName).Success
                        && !childState.Contains("invisible"))
                        return child;
                    if (childName != null 
                        && objName.Match(childName).Success)
                        return child;
                if (ignoreInvisible)
                    if (!childState.Contains("invisible"))
                        objToReturn = GetObjectByName(child, objName, ignoreInvisible);
                        if (objToReturn != default(IAccessible))
                            return objToReturn;
                    objToReturn = GetObjectByName(child, objName, ignoreInvisible);
                    if (objToReturn != default(IAccessible))
                        return objToReturn;
        return objToReturn;

How to Get the State

This is a tricky part. IAccessible.get_accState(object varChild) returns a state ID number which is a combination of one or multiple states. E.g., "MS Word 2007" has three states at a particular instance: focusable, moveable, sizeable. But, the Win32 API GetStateText will return only the first state string against the state ID. So, we have to call this API more than once. How we do it is shown in the following code snippet.


public static string GetStateText(uint stateID)
    uint maxLength = 1024;
    var focusableStateText = new StringBuilder((int)maxLength);
    var sizeableStateText = new StringBuilder((int)maxLength);
    var moveableStateText = new StringBuilder((int)maxLength);
    var invisibleStateText = new StringBuilder((int)maxLength);

    //This pattern needs to be used to fetch other available cobination of states. 
    if (stateID == (MSAAStateConstants.STATE_SYSTEM_FOCUSABLE
                   | MSAAStateConstants.STATE_SYSTEM_SIZEABLE
                   | MSAAStateConstants.STATE_SYSTEM_MOVEABLE))
                           focusableStateText, maxLength);
                           sizeableStateText, maxLength);
                           moveableStateText, maxLength);

        return focusableStateText + "," + sizeableStateText + "," + moveableStateText;


    var stateText = new StringBuilder((int)maxLength);
    Win32.GetStateText(stateID, stateText, maxLength);
    return stateText.ToString();

MSAAUIItem answers these questions and abstracts out other complexities as well.

Office 2007 UI Automation Object Model

As the name sounds, this layer is specific to the Windows application which needs to be automated. This layer extends MSAAUIItem to define the Office 2007 UI controls.


OfficeUIItem extends MSAAUIItem and acts as the core Office UI item. All other items like OfficeRibbon, OfficeToolBar, OfficeRibbonTab, OfficePropertyPage, and MSWordWindow extend OfficeUIItem.

Now, let us write some automation code. This code will launch MS Word 2007 and will open the tab named "Insert". Then, it will run the "Cover Page" command on the "Pages" Tool Bar. It is important to select a particular tab because only then MS Word will refresh the UI and load the corresponding toolbars and controls under that.

MSWordWindow msWordMainWindow = new MSWordWindow("Document1 -");
msWordMainWindow.ToolBars["Pages"].Controls["Cover Page"].Invoke();

Attached Sample Code

The sample code attached with this article is tested on Windows XP SP2 and Windows 2008. The attached code will work for Windows XP but may fail for Windows 2008. The reason for this is that the Win32 API which we use to get the Accessible Object is different for both OSs.


//Windows XP
internal static extern int AccessibleObjectFromWindow(
     IntPtr hwnd,
     uint id,
     ref Guid iid,
     [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppvObject);    

//Windows 2008
[DllImport("oleacc.dll", PreserveSig = false)]
[return: MarshalAs(UnmanagedType.Interface)]
public static extern object AccessibleObjectFromWindow(int hwnd, 
                            int dwId, ref Guid riid);


Microsoft Active Accessibility is not a comprehensive solution to automate the Windows UI. There are certain limitations. E.g., using accessibility, we can search for a TextBox control on MS Word Ribbon, but we can not get or set a value in it, which can be done using Microsoft UIA. So, a combination of MSAA and UIA can provide a more promising solution for Windows UI automation.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Software Developer Proteans Software Solutions
India India
Currently, I am working with Proteans Software Solutions

Proteans a CAMO group company is an outsourcing company focusing on software product development and business application development on Microsoft Technology Platform. "Committed to consistently deliver high-quality software products and services through continual improvement of our knowledge and practices focused on increased customer satisfaction.

Before this, I started my Development career with Quark Media House Pvt. Ltd.

I have worked in various domains like Publishing, Document Management and Health Care.

I have gained experience in Development, Debugging, Bug fixing, Memory leak fixing and Performance Consultancy.

I am working on .NET technologies.

You may also be interested in...


Comments and Discussions

QuestionHidden controls Pin
Member 87868013-Aug-17 14:39
memberMember 87868013-Aug-17 14:39 
QuestionValuable informaiton Pin
rnvc3-Feb-17 2:07
memberrnvc3-Feb-17 2:07 
QuestionNeed help for steps to access VCL controls as MSAA controls for Coded UI Pin
Member 134597326-Jul-14 6:52
memberMember 134597326-Jul-14 6:52 
QuestionWorking with Office 2010 Pin
Madhukar Gubba27-Feb-14 20:14
memberMadhukar Gubba27-Feb-14 20:14 
Questioncan not download Pin
justpower27-Apr-13 0:38
memberjustpower27-Apr-13 0:38 
GeneralMy vote of 5 Pin
ShlomiO19-Nov-12 22:32
memberShlomiO19-Nov-12 22:32 
QuestionIt seems to me like the MSAARoles class is out of date Pin
Chris Robert Mead22-May-12 11:57
memberChris Robert Mead22-May-12 11:57 

Thanks for the article. It is very helpful. I am trying out the code in the 'core' project and have noticed that the roles don't seem to be current. From the file:
C:\Program Files\Microsoft SDKs\Windows\v7.1\Include\OleAcc.h

Line 422: #define ROLE_SYSTEM_TITLEBAR ( 0x1 )
Line 424: #define ROLE_SYSTEM_MENUBAR ( 0x2 )
Line 426: #define ROLE_SYSTEM_SCROLLBAR ( 0x3 )
Line 428: #define ROLE_SYSTEM_GRIP ( 0x4 )
Line 430: #define ROLE_SYSTEM_SOUND ( 0x5 )
Line 432: #define ROLE_SYSTEM_CURSOR ( 0x6 )
Line 434: #define ROLE_SYSTEM_CARET ( 0x7 )
Line 436: #define ROLE_SYSTEM_ALERT ( 0x8 )
Line 438: #define ROLE_SYSTEM_WINDOW ( 0x9 )
Line 440: #define ROLE_SYSTEM_CLIENT ( 0xa )
Line 442: #define ROLE_SYSTEM_MENUPOPUP ( 0xb )
Line 444: #define ROLE_SYSTEM_MENUITEM ( 0xc )
Line 446: #define ROLE_SYSTEM_TOOLTIP ( 0xd )
Line 448: #define ROLE_SYSTEM_APPLICATION ( 0xe )
Line 450: #define ROLE_SYSTEM_DOCUMENT ( 0xf )
Line 452: #define ROLE_SYSTEM_PANE ( 0x10 )
Line 454: #define ROLE_SYSTEM_CHART ( 0x11 )
Line 456: #define ROLE_SYSTEM_DIALOG ( 0x12 )
Line 458: #define ROLE_SYSTEM_BORDER ( 0x13 )
Line 460: #define ROLE_SYSTEM_GROUPING ( 0x14 )
Line 462: #define ROLE_SYSTEM_SEPARATOR ( 0x15 )
Line 464: #define ROLE_SYSTEM_TOOLBAR ( 0x16 )
Line 466: #define ROLE_SYSTEM_STATUSBAR ( 0x17 )
Line 468: #define ROLE_SYSTEM_TABLE ( 0x18 )
Line 470: #define ROLE_SYSTEM_COLUMNHEADER ( 0x19 )
Line 472: #define ROLE_SYSTEM_ROWHEADER ( 0x1a )
Line 474: #define ROLE_SYSTEM_COLUMN ( 0x1b )
Line 476: #define ROLE_SYSTEM_ROW ( 0x1c )
Line 478: #define ROLE_SYSTEM_CELL ( 0x1d )
Line 480: #define ROLE_SYSTEM_LINK ( 0x1e )
Line 482: #define ROLE_SYSTEM_HELPBALLOON ( 0x1f )
Line 484: #define ROLE_SYSTEM_CHARACTER ( 0x20 )
Line 486: #define ROLE_SYSTEM_LIST ( 0x21 )
Line 488: #define ROLE_SYSTEM_LISTITEM ( 0x22 )
Line 490: #define ROLE_SYSTEM_OUTLINE ( 0x23 )
Line 492: #define ROLE_SYSTEM_OUTLINEITEM ( 0x24 )
Line 494: #define ROLE_SYSTEM_PAGETAB ( 0x25 )
Line 496: #define ROLE_SYSTEM_PROPERTYPAGE ( 0x26 )
Line 498: #define ROLE_SYSTEM_INDICATOR ( 0x27 )
Line 500: #define ROLE_SYSTEM_GRAPHIC ( 0x28 )
Line 502: #define ROLE_SYSTEM_STATICTEXT ( 0x29 )
Line 504: #define ROLE_SYSTEM_TEXT ( 0x2a )
Line 506: #define ROLE_SYSTEM_PUSHBUTTON ( 0x2b )
Line 508: #define ROLE_SYSTEM_CHECKBUTTON ( 0x2c )
Line 510: #define ROLE_SYSTEM_RADIOBUTTON ( 0x2d )
Line 512: #define ROLE_SYSTEM_COMBOBOX ( 0x2e )
Line 514: #define ROLE_SYSTEM_DROPLIST ( 0x2f )
Line 516: #define ROLE_SYSTEM_PROGRESSBAR ( 0x30 )
Line 518: #define ROLE_SYSTEM_DIAL ( 0x31 )
Line 520: #define ROLE_SYSTEM_HOTKEYFIELD ( 0x32 )
Line 522: #define ROLE_SYSTEM_SLIDER ( 0x33 )
Line 524: #define ROLE_SYSTEM_SPINBUTTON ( 0x34 )
Line 526: #define ROLE_SYSTEM_DIAGRAM ( 0x35 )
Line 528: #define ROLE_SYSTEM_ANIMATION ( 0x36 )
Line 530: #define ROLE_SYSTEM_EQUATION ( 0x37 )
Line 532: #define ROLE_SYSTEM_BUTTONDROPDOWN ( 0x38 )
Line 534: #define ROLE_SYSTEM_BUTTONMENU ( 0x39 )
Line 538: #define ROLE_SYSTEM_WHITESPACE ( 0x3b )
Line 540: #define ROLE_SYSTEM_PAGETABLIST ( 0x3c )
Line 542: #define ROLE_SYSTEM_CLOCK ( 0x3d )
Line 544: #define ROLE_SYSTEM_SPLITBUTTON ( 0x3e )
Line 546: #define ROLE_SYSTEM_IPADDRESS ( 0x3f )
Line 548: #define ROLE_SYSTEM_OUTLINEBUTTON ( 0x40 )

Am I missing something or is this a more current list? I am using a 64bit Windows 7 machine.

GeneralMy vote of 5 Pin
Abinash Bishoyi5-Feb-12 5:55
memberAbinash Bishoyi5-Feb-12 5:55 
QuestionUI Automation- How to click on text Pin
smashgeek28-Apr-11 21:38
membersmashgeek28-Apr-11 21:38 
GeneralWin 7 / VS 2010 Pin
jharrop@gmail.com18-Apr-10 22:31
memberjharrop@gmail.com18-Apr-10 22:31 
GeneralI really liked the topic Pin
manu.arora17-Aug-09 4:20
membermanu.arora17-Aug-09 4:20 
GeneralThis is great but... Pin
NideshTheDeveloper16-Aug-09 16:56
memberNideshTheDeveloper16-Aug-09 16:56 
GeneralRe: This is great but... Pin
Arshad_Ali_alizproarts16-Aug-09 19:06
memberArshad_Ali_alizproarts16-Aug-09 19:06 
GeneralHanging on to old controls Pin
gottcoder14-Aug-09 11:54
membergottcoder14-Aug-09 11:54 
GeneralRe: Hanging on to old controls Pin
Arshad_Ali_alizproarts14-Aug-09 21:17
memberArshad_Ali_alizproarts14-Aug-09 21:17 
GeneralRe: Hanging on to old controls Pin
gottcoder19-Aug-09 12:15
membergottcoder19-Aug-09 12:15 
GeneralRe: Hanging on to old controls Pin
gottcoder20-Aug-09 12:57
membergottcoder20-Aug-09 12:57 
GeneralRe: Hanging on to old controls Pin
Arshad_Ali_alizproarts20-Aug-09 18:29
memberArshad_Ali_alizproarts20-Aug-09 18:29 
GeneralRe: Hanging on to old controls Pin
gottcoder20-Aug-09 19:31
membergottcoder20-Aug-09 19:31 
GeneralRe: Hanging on to old controls Pin
Arshad_Ali_alizproarts21-Aug-09 2:57
memberArshad_Ali_alizproarts21-Aug-09 2:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.170813.1 | Last Updated 11 Aug 2009
Article Copyright 2009 by Arshad_Ali_alizproarts
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid