Figures
Figure 3 Active Accessibility Support Functions

#include <windows.h>
#include <oleacc.h>        // General Active Accessebility support
#include <winable.h>       // WinEvents support

#ifdef _DEBUG
#include <stdio.h>         // sprintf is defined in this file
#endif

#define WM_TARGET_WINDOW_FOUND (WM_APP + 1)

const char  szMainTitleCommon[] = "Find: All Files";
const char  szMainTitleW2K[] = "Search Results";
const char  *lpstrMainTitle;

//  GetUIElementName()
UINT GetUIElementName(IAccessible* pacc, VARIANT* pvarChild, 
                      LPTSTR lpszName, UINT cchName)
{
    HRESULT hr;
    BSTR bstrName;

    *lpszName = 0;
    bstrName = NULL;

    hr = pacc->get_accName(*pvarChild, &bstrName);
    
    if (hr == S_OK && bstrName)
    {
        WideCharToMultiByte(CP_ACP, 0, bstrName, -1, 
                            lpszName, cchName, NULL, NULL);
        SysFreeString(bstrName);
    }
    else
        lstrcpyn(lpszName, "unknown name", cchName);

    return(lstrlen(lpszName));
} 

//  GetUIElementRole()
UINT GetUIElementRole(IAccessible* pacc, VARIANT* pvarChild, LPTSTR lpszRole, UINT cchRole, DWORD* dwRole)
{
    HRESULT hr;
    VARIANT varRetVal;

    *lpszRole = 0;

    VariantInit(&varRetVal);

    hr = pacc->get_accRole(*pvarChild, &varRetVal);
    
    if (hr == S_OK && varRetVal.vt == VT_I4)
    {
        GetRoleText(varRetVal.lVal, lpszRole, cchRole);
        *dwRole = varRetVal.lVal;
    }
    else
        lstrcpyn(lpszRole, "unknown role", cchRole);

    VariantClear(&varRetVal);

    return(lstrlen(lpszRole));
}

//  GetUIElementState()
UINT GetUIElementState(IAccessible* pacc, VARIANT* pvarChild, 
                       LPTSTR lpszState, UINT cchState, DWORD* dwState)
{
    HRESULT hr;
    VARIANT varRetVal;

    *lpszState = 0;

    VariantInit(&varRetVal);

    if(S_OK != (hr = pacc->get_accState(*pvarChild, &varRetVal)))
        return(0);

    DWORD dwStateBit;
    UINT cChars = 0;     // number of characters in the lpszState string
    UINT cCount = 32;    // number of bits

    if (varRetVal.vt == VT_I4)
    {
        *dwState = varRetVal.lVal;

        // Treat the "normal" state (varRetVal.vt = 0) separately
        if(varRetVal.vt == 0)
        {
            GetStateText(0, lpszState, cChars);
        }
        else
        {
            // Convert state flags to comma separated list.
            for(dwStateBit = 1; cCount; cCount--, dwStateBit <<= 1)
            {
                if (varRetVal.lVal & dwStateBit)
                {
                    cChars += GetStateText(dwStateBit, lpszState + 
                                           cChars, cchState - cChars);
                    if(cchState > cChars)
                        *(lpszState + cChars++) = ',';
                    else
                        break;
                }

            }
            if(cChars > 1)
                *(lpszState + cChars - 1) = '\0';
        }
    }

    VariantClear(&varRetVal);

    return(lstrlen(lpszState));
}

//  GetWindowClassForUIElement()
//  This gets the Window Class of an object.
UINT GetWindowClassForUIElement(IAccessible* pacc, LPTSTR lpszClass, 
                                UINT cchClass)
{
    HRESULT hr;
    HWND hWnd;

    *lpszClass = 0;

    if(S_OK == (hr = WindowFromAccessibleObject(pacc,  &hWnd)))
    {
        if(hWnd)
            GetClassName(hWnd, lpszClass, cchClass);
        else
            lstrcpyn(lpszClass, "No window", cchClass);
    }

    return (lstrlen(lpszClass));
}
//  FindChild()
//  Finds the child

    BOOL FindChild (IAccessible* paccParent, LPCSTR szName, DWORD dwRole, 
        LPCSTR szClass, IAccessible** ppaccChild, VARIANT* pvarChild)
{
    IEnumVARIANT* pEnum = NULL;
    IDispatch* pDisp = NULL;

    IAccessible* paccChild = NULL;
    VARIANT varChild;

    HRESULT hr;
    BOOL found = FALSE;
    long numChildren = 0;
    unsigned long numFetched;
    int index;
    DWORD dwObjState, dwObjRole;
    char szObjName[256], szObjRole[256], szObjClass[256], 
        szObjState[256];

    // Check if IEnumVARIANT is supported...
    hr = paccParent -> QueryInterface(IID_IEnumVARIANT, 
                                      (PVOID*) & pEnum);

    if(hr == S_OK && pEnum) pEnum -> Reset();

    // Get child count
    hr = paccParent -> get_accChildCount(&numChildren);

    // If there's no children (or an error), bail out now.
    if(hr != S_OK || numChildren == 0) return found;
    
    for(index = 1; index <= numChildren && !found; index++)
    {
        paccChild = NULL;
        
        // Get next child - use the IEnumVARIANT if it is available, 
        // otherwise use the current ordinal.
        if (pEnum)
        {
            if(S_OK != (hr = pEnum -> Next(1, &varChild, &numFetched)))
                continue;
        }
        else
        {
            varChild.vt = VT_I4;
            varChild.lVal = index;
        }

        // See if we can get an IDispatch interface for the child
        if (varChild.vt == VT_I4)
        {
            pDisp = NULL;
            // Whenever we get a VT_I4 back, we always have to
            // call get get_accChild to check if it actually
            // has a corresponding IAccessible of its own.
            paccParent -> get_accChild(varChild, &pDisp);
        }
        else if(varChild.vt == VT_DISPATCH)
        {
            pDisp = varChild.pdispVal;
        }
        else
        {
            VariantClear(&varChild);
            continue;
        }

        // If we got an IDispatch above, convert it to an IAccessible 
        // using QI
        if (pDisp)
        {
            hr = pDisp->QueryInterface(IID_IAccessible,
                                       (void**)&paccChild);
            pDisp->Release();
            if(hr != S_OK)
                continue;
        }

        // If paccChild is non-NULL here, then this child has an 
        // IAccessible of its own - we use it with CHILDID_SELF to 
        // represent the child. If paccChild is NULL, we use the parent's 
        // interface, with the VT_I4 variant from above.
        if(paccChild)
        {
            VariantInit(&varChild);
            varChild.vt = VT_I4;
            varChild.lVal = CHILDID_SELF;
        }
        else
        {
            paccChild = paccParent;
            paccChild->AddRef();
        }

        // At this point, (paccChild, varChild) represents the child 
        // element. (If we don't return paccChild, we must release it 
        // ourselves.)
        GetWindowClassForUIElement(paccChild, szObjClass, sizeof(szObjClass));
        
        // Skip unavailable objects and their children
        if(dwObjState & STATE_SYSTEM_UNAVAILABLE)
        {
            if(paccChild) paccChild->Release();
            continue;
        }

        GetUIElementState(paccChild, &varChild, szObjState, 
                          sizeof(szObjState), &dwObjState);
        GetUIElementName(paccChild, &varChild, szObjName, sizeof(szObjName));
        GetUIElementRole(paccChild, &varChild, szObjRole, sizeof(szObjRole),
                         &dwObjRole);

        // Do the name, role and class (if specified) match? If so, we've 
        // found the element we're looking for.
        if ((!szName || !strcmp(szName, szObjName))
           && ((dwRole == 0) || (dwRole == dwObjRole))
           && (!szClass || !stricmp(szClass, szObjClass)))
        {
            // Found it - transfer the IAccessible and VARIANT to the out 
            // params, and exit the loop.
            found = TRUE;
            *ppaccChild = paccChild;
            *pvarChild = varChild;

            // The next line is for debugging purposes only
            #ifdef _DEBUG
            char szDebugString[526];
            sprintf(szDebugString, "Found child has name='%s', role='%s', 
                    class = '%s', state = '%s'\n", szObjName, szObjRole, 
                    szObjClass, szObjState);
            OutputDebugString(szDebugString);
            #endif
            break;
            
        }

        // If this child element has children, recursively check them for 
        // a match. (The element can only have children if its childid 
        // is CHILDID_SELF.)
        if( varChild.lVal == CHILDID_SELF )
            found = FindChild(paccChild, szName, dwRole, szClass, 
                              ppaccChild, pvarChild);

        paccChild->Release();
    }

    // Clean up
    if(pEnum) pEnum -> Release();

    return found;
}

//  WinEventProc()
void CALLBACK WinEventProc
(
    HWINEVENTHOOK  hEvent,
    DWORD          event,
    HWND           hwndMsg,
    LONG           idObject,
    LONG           idChild,
    DWORD          idThread,
    DWORD          dwmsEventTime
)
{

    if((event != EVENT_OBJECT_CREATE && event != EVENT_OBJECT_SHOW)  || 
        hwndMsg == NULL)
        return;

    char bufferName[256];
    IAccessible *pacc=NULL;
    VARIANT varChild;
    VariantInit(&varChild);
    HRESULT hr= AccessibleObjectFromEvent(hwndMsg, idObject, idChild, 
                                          &pacc, &varChild);

    if(!SUCCEEDED(hr))
    {
        VariantClear(&varChild);
        return;
    }

    GetUIElementName(pacc, &varChild, bufferName, sizeof(bufferName));

    // We use strstr() to provide partial-name search.  We might not know
    // the exact name for a window, just a part of it. 
    if(strstr(bufferName, lpstrMainTitle))
        PostThreadMessage(GetCurrentThreadId(), WM_TARGET_WINDOW_FOUND, 0, 0);
    
    pacc->Release();

    return;
}

int FAR PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                       LPTSTR lpCmdLine, int nCmdShow)
{
    HRESULT hr;

    HWINEVENTHOOK  hEventHook = NULL;

    IAccessible *paccMainWindow = NULL;
    IAccessible*    paccControl = NULL;
    VARIANT            varControl;

    HWND hWndMainWindow;
    HWND hWnd;

    BOOL bIsWin2K;
    char *lpstrName;
    char *lpstrWindowsClass;

    // Get the version of the operating system that is currently running
    OSVERSIONINFO OSVersionInfo;
    OSVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    GetVersionEx(&OSVersionInfo);

    if(OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT && 
       OSVersionInfo.dwMajorVersion == 5)
    {
        lpstrMainTitle = szMainTitleW2K;
        bIsWin2K = TRUE;
    }
    else
    {
        lpstrMainTitle = szMainTitleCommon;
        bIsWin2K = FALSE;
    }
    
    CoInitialize(NULL);

    if(NULL == (hWndMainWindow = FindWindow(NULL, lpstrMainTitle)))
    {
        hEventHook = SetWinEventHook(
            EVENT_MIN,
            EVENT_MAX,    //end event id
            NULL,         //always NULL for outprocess hook 
            WinEventProc,
            0,            //hooked into
            0,            //idThread, //hooked into
            //always the same for outproc hook
            WINEVENT_SKIPOWNPROCESS | WINEVENT_OUTOFCONTEXT); 
    }
    else
        PostThreadMessage(GetCurrentThreadId(), WM_TARGET_WINDOW_FOUND, 0, 0);

    MSG msg;
    while (GetMessage (&msg, NULL, 0, 0) )
    {

        if(msg.message == WM_TARGET_WINDOW_FOUND)
        {
            if(hEventHook)
            {
                UnhookWinEvent(hEventHook);
                hWndMainWindow = FindWindow(NULL, lpstrMainTitle);
            }

            // Bring the window to foreground
            SetForegroundWindow(hWndMainWindow);
            
            if(S_OK == (hr = AccessibleObjectFromWindow(hWndMainWindow, 
               OBJID_WINDOW, IID_IAccessible,(void**) &paccMainWindow)))
            {
                // Set Containing text:
                if(FindChild (paccMainWindow, "Containing text:", 
                   ROLE_SYSTEM_TEXT, "Edit", &paccControl, &varControl))
                {
                    WindowFromAccessibleObject(paccControl, &hWnd);
                    // We use SendMessage(..., WM_SETTEXT,...) instead of 
                    // SetWindowText(), since we set text for a different 
                    // process
                    SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)"My Text");
                    paccControl->Release();
                    VariantClear(&varControl);
                }

                // Set Named:
                if(bIsWin2K)
                {
                    lpstrName = "Search for files or folders named:";
                    lpstrWindowsClass = "Edit";
                }
                else
                {
                    lpstrName = "Named:";
                    lpstrWindowsClass = "ComboBox";
                }

                if(FindChild (paccMainWindow, lpstrName, 
                              ROLE_SYSTEM_WINDOW, lpstrWindowsClass, 
                              &paccControl, &varControl))
                {
                    WindowFromAccessibleObject(paccControl, &hWnd);
                    // We use SendMessage(..., WM_SETTEXT,...) instead of 
                    // SetWindowText(), since we set text for a different 
                    // process
                    SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)"MyFile.cpp");
                    paccControl->Release();
                    VariantClear(&varControl);
                }

                // Click the Find Now button:
                if(bIsWin2K)
                {
                    lpstrName = "Search Now";
                }
                else
                {
                    lpstrName = "Find Now";
                }

                if(FindChild (paccMainWindow, lpstrName,        
                              ROLE_SYSTEM_PUSHBUTTON, "Button", 
                              &paccControl, &varControl))
                {
                    hr = paccControl->accDoDefaultAction(varControl);
                    paccControl->Release();
                    VariantClear(&varControl);
                }

                paccMainWindow->Release();
            }

            MessageBox(NULL, "Click OK to finish the demo", 
                       "That's all folks", MB_OK);

            // ... and press Alt+F4 to close the window
            INPUT input[4];    
            memset(input, 0, sizeof(input));

            input[0].type = input[1].type = input[2].type = 
            input[3].type = INPUT_KEYBOARD;
            input[0].ki.time = input[1].ki.time = input[2].ki.time = 
                input[3].ki.time = GetTickCount();

            input[0].ki.wVk  = input[2].ki.wVk = VK_MENU;
            input[1].ki.wVk  = input[3].ki.wVk = VK_F4;

            // Then release it. THIS IS IMPORTANT
            input[2].ki.dwFlags = input[3].ki.dwFlags = KEYEVENTF_KEYUP;

            SendInput(4, input, sizeof(INPUT));
            
            return 1;
        }

        TranslateMessage (&msg);
        DispatchMessage (&msg);
    }

    return 0;
}
Figure 4 Verifying IAccessible Support

if (pEnum)
{
   if(S_OK != (hr = pEnum -> Next(1, &varChild, &numFetched)))
      continue;
}
else
{
    varChild.vt = VT_I4;
    varChild.lVal = index;
}

// See if we can get an IDispatch interface for the child
if (varChild.vt == VT_I4)
{
    pDisp = NULL;
    // Whenever we get a VT_I4 back, we always have to
    // call get_accChild to check if it actually
    // has a corresponding IAccessible of its own.
    paccParent -> get_accChild(varChild, &pDisp);
}
else if(varChild.vt == VT_DISPATCH)
{
    pDisp = varChild.pdispVal;
}
else
{
    VariantClear(&varChild);
    continue;
}
// If we got an IDispatch above, convert it to an IAccessible using QI
if (pDisp)
{
    hr = pDisp->QueryInterface(IID_IAccessible, (void**)&paccChild);
    pDisp->Release();
    if(hr != S_OK) continue;
}
Figure 5 Button Click Emulation

long xLeft, yTop, cxWidth, cyHeight;
HRESULT hr;

hr = paccChild->accLocation(&xLeft,  &yTop,  &cxWidth,  &cyHeight,  
                            varChild);

// Click
if(hr == S_OK)
{
    INPUT input[2];    
    memset(input, 0, sizeof(input));
        
    // Mouse move
    SetCursorPos(xLeft + 5, yTop + cyHeight/2);

    // Fill the structure
    input[0].type = INPUT_MOUSE;
    input[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
    input[0].mi.dwExtraInfo = 0;
    input[0].mi.dx = 0;
    input[0].mi.dy = 0;
    input[0].mi.time = GetTickCount();

    // All inputs are almost the same
    memcpy(&input[1], &input[0], sizeof(INPUT));
       
    // ... almost
    input[1].mi.dwFlags = MOUSEEVENTF_LEFTUP;
 
    SendInput(1, input, sizeof(INPUT));
    Sleep(100);
    SendInput(1, input+1, sizeof(INPUT));

}
Figure 6 Handling UI Elements-Main Thread

HRESULT STA_WindowFromAccessibleObject( IAccessible * pAcc, 
                                        HWND * phwnd )
{
    // Try the local version first...
    HRESULT hr = WindowFromAccessibleObject( pAcc, phwnd );
    if( hr != RPC_E_CANTCALLOUT_ININPUTSYNCCALL )
        return hr; // was a local object (or some other error)
    
    IStream * pstm = NULL;
    hr = CoMarshalInterThreadInterfaceInStream( IID_IAccessible, 
                                                pAcc, &pstm );

    STA_WFAO_PARAMS xp;
    xp.pstm = pstm;
    xp.phwnd = phwnd;

    // NB - can't use SendMessage here because the recipient 
    // won't be able to make SYNC calls.
    PostThreadMessage( g_STA_idThread, WM_USER, (WPARAM) & xp, 0 );
    // Now wait for the thread to finish processing the request...
    WaitForSingleObject( g_STA_hEv, INFINITE );

    return xp.hr;
}
Figure 7 Handling UI Elements-Helper Thread

DWORD WINAPI STA_ThreadStart( void * )
{
    // Create this thread in an apartment of its own...
    CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );

    HANDLE hEv = OpenEvent( EVENT_MODIFY_STATE, FALSE, 
                            TEXT("STA_ThreadEvent") );
    SetEvent( hEv );

    // Since this is an STA thread, we must have a message loop...
    MSG msg;
    while( GetMessage( & msg, NULL, 0, 0 ) )
    {
        // Is this a request to do a WindowFromAccessibleObject?
        if( msg.message == WM_USER )
        {
        STA_WFAO_PARAMS * pxp = (STA_WFAO_PARAMS *) msg.wParam;

          // Unmarshall the IAccessible...
          IAccessible * pAcc = NULL;
          HRESULT hr = CoGetInterfaceAndReleaseStream( pxp->pstm, 
              IID_IAccessible, (void **) & pAcc );

          hr = WindowFromAccessibleObject( pAcc, pxp->phwnd );
          pAcc->Release();

          // Set the return value, and notify caller via event  
          // that we're done.
          pxp->hr = hr;
          SetEvent( hEv );
        }
        else
        {
          TranslateMessage( & msg );
          DispatchMessage( & msg );
        }
    }

    CloseHandle( hEv );
    CoUninitialize();
    return 0;
}
Figure 8 Other Active Accessibility Functions
get_accParent Retrieves the IDispatch interface of the current object's parent. This function is used to navigate up through the Active Accessibility tree or to get information about the parent.

accNavigate Retrieves the next or previous sibling or child object in a specified direction. It's convenient if you need to navigate based on spatial or logical relationships between objects. Note that you can't be sure of the hierarchical relationships between two objects near one another on the screen.

get_accHelp Retrieves an object's help property string. This works in the same way as all Active Accessibility functions that retrieve information stored in Active Accessibility properties, such as get_accName, get_accRole, and so on.

get_accDescription Retrieves a textual description for an object's visual appearance. The description is primarily used to provide greater context for visually impaired or blind users, but can also be used for context searching or other applications.

get_accHelpTopic Retrieves the full path of the help file associated with the specified object, as well as the address of the appropriate topic within that file. get_accFocus Retrieves the child object that currently has the keyboard focus. This function returns a VARIANT that contains either a pointer to the IDispatch interface or a child ID. To get the (IAccessible*, VARIANT) pair corresponding to the object that has a focus, the value returned by this function should be processed in the same way as the return value of the Next method in the FindChild function from the sample program.

get_accSelection Retrieves the selected children of the object. For a single selection, the return value of this function is a VARIANT and is similar to the return value of get_accFocus. If multiple objects were selected, get_accSelection returns an IEnumVARIANT containing multiple selected children.

get_accKeyboardShortcut Retrieves an object's KeyboardShortcut property. The return value is a BSTR that contains the keyboard shortcut string. Although this function is designed for human consumption, it might be convenient for performing an action on controls that support keyboard shortcuts. Once you have a string that represents a shortcut, you can parse this string and use SendInput to emulate the keyboard input. Note that it may be difficult to parse the string automatically. It may also be necessary to use other keys or actions to set up the focus appropriately. The returned shortcut key usually assumes that the parent object has focus.

accSelect Modifies the selection or moves the keyboard focus according to the specified flags. The first argument is a combination of the values from the SELFLAG enumerated type. This type contains the following values: SELFLAG_NONE, SELFLAG_ TAKEFOCUS, SELFLAG_TAKESELECTION, SELFLAG_EXTENDSELECTION, SELFLAG_ADDSELECTION, and SELFLAG_REMOVESELECTION. (See the Active Accessibility SDK documentation for details.)

accHitTest Retrieves the child object at a given point on the screen. This function is analogous to the AccessibleObjectFromPoint method. The main difference is that the accHitTest method succeeds only if there is a child at the specified point. If the object that calls this method has no children at the requested screen coordinates, this function will fail. If there is a child, and this child is partially or fully covered by another UI element, this method succeeds and returns a VARIANT that defines the child. This method fails for invisible children. If there are two children overlapping at the same point, a VARIANT that defines the topmost child will be returned.
Page view tracker