/*************************************************************************
    Project:    Microsoft Active Accessibility
    Module:     EVENt.CPP

    Author:     LauraBu, SteveDon, PeteW
    Date:       5/1/96
    
    Notes:
      Accessibility Event trapper, gets them in or out of context, depending
      on how the app calls it - set via a menu option.

    Copyright (C) 1996 by Microsoft Corporation.  All rights reserved.
    See bottom of file for disclaimer
    
    History:    

*************************************************************************/
#include <windows.h>
#include <windowsx.h>
#include <winable.h>
#include <initguid.h>
#include <objbase.h>
#include <objerror.h>
#include <ole2.h>
#include <oleacc.h>
#include "event.h"

//BUGBUG: This is undocumented!
#define HEAP_SHARED     0x04000000      // Win95 only

//
// Per-process Variables
//
HMODULE     hModEventDll;
const TCHAR szNameless[] = "(nameless)";


// Shared Variables
#pragma data_seg(".sdata")
HWINEVENTHOOK	hEventHook = NULL;
HWND			hwndEventPost = NULL;
HANDLE			hheapShared = NULL;
UINT			grfTrack = 0;
#pragma data_seg()



//
// Functions
//
void CALLBACK
NotifyProc(HWINEVENTHOOK hEvent, DWORD event, HWND hwndMsg, LONG idObject,
    LONG idChild, DWORD idThread, DWORD dwEventTime);

// --------------------------------------------------------------------------
//
//  DllMain()
//
// --------------------------------------------------------------------------
BOOL WINAPI
DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID pvReserved)
{
    switch (dwReason)
    {
        case DLL_PROCESS_ATTACH:
            hModEventDll = hInst;
            break;

#if 0
        case DLL_THREAD_ATTACH:
            CoInitialize(NULL);
            break;

        case DLL_THREAD_DETACH:
            CoUninitialize();
            break;
#endif
    }

    return(TRUE);

}


// --------------------------------------------------------------------------
//
//  InstallEventHook
//
//	Installs or removes a WinEvent hook.
//
//  If successful, this returns the handle to the shared heap, so that the
//  receiver of the event strings can free the memory at his convenience.
//
//	Parameters:
//		hwndPostTo	This is the hwnd we will post a private message 
//					(WM_ADDEVENT) to, so that the "owner" of the hook 
//					knows there was an event processed. We process the events
//					by creating a string describing the event and placing that
//					string in the shared heap whose handle this function returns.
//					The parameters that come with this message are:
//					WPARAM = count of bytes in the event string
//					LPARAM = far pointer to the string
//					When the "owner" gets this message, he should do something
//					with the string (like add it to his list box) and then free
//					the memory the string uses with the HeapFree function.
//
//					TO REMOVE THE HOOK, call this function with hwndPostTo
//					set to NULL.
//
//		fInContext	if TRUE, the hook is installed as a WINEVENT_INCONTEXT
//					hook, so that we will process the events syncronously,
//					that is, at the same time they are happening. If FALSE,
//					the hook is installed as a WINEVENT_OUTOFCONTEXT hook,
//					and we process the events asynchronously, some time 
//					after they have occurred. Note that to change from one
//					to the other, the caller should first release the old 
//					hook and then install the new hook.
//
//	Returns:
//		A handle to the shared heap, or NULL if the call failed. When 
//		removing the hook, returns NULL if successful, or the same shared
//		heap handle if it failed.
// --------------------------------------------------------------------------
STDAPI_(HANDLE)
InstallEventHook(HWND hwndPostTo,BOOL fInContext)
{
    if (hwndPostTo)
    {
        // Installing the hook
        if (hwndEventPost)
            return(NULL);

        // Create the shared heap.
        hheapShared = HeapCreate(HEAP_SHARED, 0, 0);
        if (!hheapShared)
            return(NULL);

        // Install the hook
		if (fInContext)
			hEventHook = SetWinEventHook(EVENT_MIN, EVENT_MAX, hModEventDll,
				NotifyProc, 0, 0, WINEVENT_SKIPOWNPROCESS | WINEVENT_INCONTEXT);
		else
			hEventHook = SetWinEventHook(EVENT_MIN, EVENT_MAX, hModEventDll,
				NotifyProc, 0, 0, WINEVENT_SKIPOWNPROCESS | WINEVENT_OUTOFCONTEXT);

        if (!hEventHook)
        {
            HeapDestroy(hheapShared);
            hheapShared = NULL;
            return(NULL);
        }

        hwndEventPost = hwndPostTo;
    }
    else
    {
        hwndEventPost = NULL;

        // Uninstalling the hook
        if (hEventHook)
        {
            UnhookWinEvent(hEventHook);
            hEventHook = NULL;
        }

        if (hheapShared)
        {
            HeapDestroy(hheapShared);
            hheapShared = NULL;
        }
    }

    return(hheapShared);
}


// --------------------------------------------------------------------------
//
//  SetEventFilter()
//
//  Changes what objects we are watching events for.
//
// --------------------------------------------------------------------------
STDAPI_(void)
SetEventFilter(UINT uFlags)
{
    grfTrack = uFlags;
}



// --------------------------------------------------------------------------
//
//  DoFakeEvent()
//
//  This just generates a fake event as though a real win event had occurred
//  on this process.
//
// --------------------------------------------------------------------------
STDAPI_(void)
DoFakeEvent(UINT uEvent)
{
    NotifyProc(hEventHook, uEvent, NULL, 0, 0, GetCurrentThreadId(),
        GetCurrentTime());
}


// --------------------------------------------------------------------------
//
//  NotifyProc()
//
// --------------------------------------------------------------------------
void CALLBACK
NotifyProc
(
    HWINEVENTHOOK	hEvent,
    DWORD			event,
    HWND			hwndMsg,
    LONG			idObject,
    LONG			idChild,
    DWORD			idThread,
    DWORD			dwmsEventTime
)
{
    TCHAR   szEventText[1024];
    UINT    cchEventText;
    LPTSTR  lpEventMem;

    if (grfTrack & SEF_TRACKING)
    {
        cchEventText = ObjectProc(hEvent, event, hwndMsg, idObject, idChild,
            idThread, szEventText, ARRAYSIZE(szEventText)-1);

        if (cchEventText && hheapShared)
        {
            lpEventMem = (LPSTR)HeapAlloc(hheapShared, 0, (cchEventText+1)*sizeof(TCHAR));
            if (lpEventMem)
            {
                if ((UINT)lstrlen(szEventText) != cchEventText)
                    DebugBreak();

                lstrcpy(lpEventMem, szEventText);
                if (!PostMessage(hwndEventPost, WM_ADDEVENT, cchEventText, (LPARAM)lpEventMem))
                    HeapFree(hheapShared, 0, lpEventMem);
            }
        }
    }

    return;
}


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

    *lpszName = 0;
    bstrName = NULL;

    if (!(grfTrack & SEF_USEINVOKE))
    {
        // via IAccessible
        hr = pacc->get_accName(*pvarChild, &bstrName);
    }
    else
    {
        DISPPARAMS  dispp;
        EXCEPINFO   excepInfo;
        UINT        errArg;
        VARIANT     varResult;

        // via IDispatch::Invoke
        VariantInit(&varResult);

        dispp.cArgs = 1;
        dispp.cNamedArgs = 0;
        dispp.rgvarg = pvarChild;
        dispp.rgdispidNamedArgs = NULL;

        FillMemory(&excepInfo, sizeof(excepInfo), 0);

        hr = pacc->Invoke(DISPID_ACC_NAME, IID_NULL, 0, DISPATCH_PROPERTYGET,
            &dispp, &varResult, &excepInfo, &errArg);
        if (SUCCEEDED(hr) && (varResult.vt == VT_BSTR))
            bstrName = varResult.bstrVal;
        else
            VariantClear(&varResult);
    }

    if (SUCCEEDED(hr) && bstrName)
    {
        WideCharToMultiByte(CP_ACP, 0, bstrName, -1, lpszName, cchName, NULL, NULL);
        SysFreeString(bstrName);
    }

    if (! *lpszName)
        lstrcpy(lpszName, "<unknown>");

    return(lstrlen(lpszName));
}


// --------------------------------------------------------------------------
//
//  GetObjectBasic
//
// --------------------------------------------------------------------------
UINT GetObjectBasic(IAccessible* pacc, VARIANT* pvarChild, LPTSTR lpszOut,
    UINT cchMax)
{
    LPTSTR lpszBuf = lpszOut;
    UINT cch;
    if (!lpszBuf) return 0;
        lpszBuf += cch = GetObjectName(pacc, pvarChild, lpszBuf, cchMax-1);
    if (cch) *lpszBuf++ = ' ';
        lpszBuf += cch = GetObjectRole(pacc, pvarChild, lpszBuf, cchMax - (lpszBuf-lpszOut) - 1);
    if (cch) *lpszBuf++ = ' ';
        lpszBuf += cch = GetObjectValue(pacc, pvarChild, lpszBuf, cchMax - (lpszBuf-lpszOut - 1));
    return lstrlen(lpszOut);
}


// --------------------------------------------------------------------------
//
//  GetObjectDescription
//
// --------------------------------------------------------------------------
UINT GetObjectDescription(IAccessible* pacc, VARIANT* pvarChild, LPTSTR lpszDesc,
    UINT cchDesc)
{
    HRESULT hr;
    BSTR bstrDesc;

    *lpszDesc = 0;
    bstrDesc = NULL;

    if (!(grfTrack & SEF_USEINVOKE))
    {
        // via IAccessible
        hr = pacc->get_accDescription(*pvarChild, &bstrDesc);
    }
    else
    {
        DISPPARAMS  dispp;
        EXCEPINFO   excepInfo;
        UINT        errArg;
        VARIANT     varResult;

        // via IDispatch::Invoke
        VariantInit(&varResult);

        dispp.cArgs = 1;
        dispp.cNamedArgs = 0;
        dispp.rgvarg = pvarChild;
        dispp.rgdispidNamedArgs = NULL;

        FillMemory(&excepInfo, sizeof(excepInfo), 0);

        hr = pacc->Invoke(DISPID_ACC_DESCRIPTION, IID_NULL, 0,
            DISPATCH_PROPERTYGET, &dispp, &varResult, &excepInfo, &errArg);
        if (SUCCEEDED(hr) && (varResult.vt == VT_BSTR))
            bstrDesc = varResult.bstrVal;
        else
            VariantClear(&varResult);
    }

    if (! SUCCEEDED(hr))
        return(0);

    if (bstrDesc)
    {
        WideCharToMultiByte(CP_ACP, 0, bstrDesc, -1, lpszDesc, cchDesc, NULL, NULL);
        SysFreeString(bstrDesc);
    }

    return(lstrlen(lpszDesc));
}


// --------------------------------------------------------------------------
//
//  GetObjectValue
//
// --------------------------------------------------------------------------
UINT GetObjectValue(IAccessible* pacc, VARIANT* pvarChild, LPTSTR lpszValue,
    UINT cchValue)
{
    HRESULT hr;
    BSTR bstrValue;

    *lpszValue = 0;
    bstrValue = NULL;

    if (!(grfTrack & SEF_USEINVOKE))
    {
        // via IAccessible
        hr = pacc->get_accValue(*pvarChild, &bstrValue);
    }
    else
    {
        DISPPARAMS  dispp;
        EXCEPINFO   excepInfo;
        UINT        errArg;
        VARIANT     varResult;

        // via IDispatch::Invoke
        VariantInit(&varResult);

        dispp.cArgs = 1;
        dispp.cNamedArgs = 0;
        dispp.rgvarg = pvarChild;
        dispp.rgdispidNamedArgs = NULL;

        FillMemory(&excepInfo, sizeof(excepInfo), 0);

        hr = pacc->Invoke(DISPID_ACC_VALUE, IID_NULL, 0, DISPATCH_PROPERTYGET,
            &dispp, &varResult, &excepInfo, &errArg);
        if (SUCCEEDED(hr) && (varResult.vt == VT_BSTR))
            bstrValue = varResult.bstrVal;
        else
            VariantClear(&varResult);
    }

    if (!SUCCEEDED(hr))
        return(0);

    if (bstrValue)
    {
        WideCharToMultiByte(CP_ACP, 0, bstrValue, -1, lpszValue, cchValue, NULL, NULL);
        SysFreeString(bstrValue);
    }

    return(lstrlen(lpszValue));
}


// --------------------------------------------------------------------------
//
//  GetObjectRole()
//
// --------------------------------------------------------------------------
UINT GetObjectRole(IAccessible* pacc, VARIANT* pvarChild, LPTSTR lpszRole,
    UINT cchRole)
{
    HRESULT hr;
    VARIANT varRetVal;

    *lpszRole = 0;

    VariantInit(&varRetVal);

    if (!(grfTrack & SEF_USEINVOKE))
    {
        // via IAccessible
        hr = pacc->get_accRole(*pvarChild, &varRetVal);
    }
    else
    {
        DISPPARAMS  dispp;
        EXCEPINFO   excepInfo;
        UINT        errArg;
    
        // via IDispatch::Invoke
        dispp.cArgs = 1;
        dispp.cNamedArgs = 0;
        dispp.rgvarg = pvarChild;
        dispp.rgdispidNamedArgs = NULL;

        FillMemory(&excepInfo, sizeof(excepInfo), 0);

        hr = pacc->Invoke(DISPID_ACC_ROLE, IID_NULL, 0, DISPATCH_PROPERTYGET,
            &dispp, &varRetVal, &excepInfo, &errArg);
    }
    if (!SUCCEEDED(hr))
        return(0);

    if (varRetVal.vt == VT_I4)
    {
        GetRoleText(varRetVal.lVal, lpszRole, cchRole);
    }
    else if (varRetVal.vt == VT_BSTR)
    {
        WideCharToMultiByte(CP_ACP, 0, varRetVal.bstrVal, -1, lpszRole,
            cchRole, NULL, NULL);
    }

    VariantClear(&varRetVal);

    return(lstrlen(lpszRole));
}


// --------------------------------------------------------------------------
//
//  GetObjectState()
//
// --------------------------------------------------------------------------
UINT GetObjectState(IAccessible* pacc, VARIANT* pvarChild, LPTSTR lpszState,
    UINT cchState)
{
    HRESULT hr;
    VARIANT varRetVal;

    *lpszState = 0;

    VariantInit(&varRetVal);

    if (!(grfTrack & SEF_USEINVOKE))
    {
        // via IAccessible
        hr = pacc->get_accState(*pvarChild, &varRetVal);
    }
    else
    {
        DISPPARAMS  dispp;
        EXCEPINFO   excepInfo;
        UINT        errArg;
        
        // via IDispatch::Invoke
        dispp.cArgs = 1;
        dispp.cNamedArgs = 0;
        dispp.rgvarg = pvarChild;
        dispp.rgdispidNamedArgs = NULL;

        FillMemory(&excepInfo, sizeof(excepInfo), 0);

        hr = pacc->Invoke(DISPID_ACC_STATE, IID_NULL, 0, DISPATCH_PROPERTYGET,
            &dispp, &varRetVal, &excepInfo, &errArg);
    }
    if (!SUCCEEDED(hr))
        return(0);

    if (varRetVal.vt == VT_I4)
    {
        int   iStateBit;
        DWORD lStateBits;
        LPSTR lpszT;

        lpszT = lpszState;

        //
        // Convert state flags to comma separated list.
        //
        for (iStateBit = 0, lStateBits = 1; iStateBit < 32; iStateBit++, (lStateBits <<= 1))
        {
            if (varRetVal.lVal & lStateBits)
            {
                lpszT += GetStateText(lStateBits, lpszT, cchState);
                *lpszT++ = ',';
                *lpszT++ = ' ';
            }
        }

        //
        // Clip off final ", "
        //
        if (varRetVal.lVal)
        {
            *(lpszT-2) = 0;
            *(lpszT-1) = 0;
        }
        else
            GetStateText(0, lpszT, cchState);
    }
    else if (varRetVal.vt == VT_BSTR)
    {
        WideCharToMultiByte(CP_ACP, 0, varRetVal.bstrVal, -1, lpszState,
            cchState, NULL, NULL);
    }

    VariantClear(&varRetVal);

    return(lstrlen(lpszState));
}


// --------------------------------------------------------------------------
//
//  GetObjectLocation()
//
// --------------------------------------------------------------------------
UINT GetObjectLocation(IAccessible* pacc, VARIANT* pvarChild,
    LPTSTR lpszLocation, UINT cchLocation)
{
    HRESULT hr;
    RECT rcL;

    *lpszLocation = 0;
    SetRectEmpty(&rcL);

    if (!(grfTrack & SEF_USEINVOKE))
    {
        // via IAccessible
        hr = pacc->accLocation(&rcL.left, &rcL.top, &rcL.right, &rcL.bottom, *pvarChild);
    }
    else
    {
        // via IDispatch::Invoke
        DISPPARAMS  dispp;
        EXCEPINFO   excepInfo;
        UINT        errArg;
        VARIANT     rgvar[5];

        FillMemory(&excepInfo, sizeof(excepInfo), 0);

        dispp.cArgs = 5;
        dispp.cNamedArgs = 0;
        dispp.rgvarg = rgvar;
        dispp.rgdispidNamedArgs = NULL;

        //
        // Arguments are BACKWARDS
        //
        for (errArg = 4; errArg >= 1; errArg--)
        {
            VariantInit(&rgvar[errArg]);
            rgvar[errArg].vt = VT_I4 | VT_BYREF;
            rgvar[errArg].plVal = &(((long *)&rcL)[4 - errArg]);
        }

        VariantInit(&rgvar[0]);
        VariantCopy(&rgvar[0], pvarChild);

        hr = pacc->Invoke(DISPID_ACC_LOCATION, IID_NULL, 0, DISPATCH_METHOD,
            &dispp, NULL, &excepInfo, &errArg);
    }
    if (!SUCCEEDED(hr))
        return(0);
        
    if (!rcL.right || !rcL.bottom)
        return(0);

    wsprintf(lpszLocation, "{%04x, %04x, %04x, %04x}", rcL.left, rcL.top,
        rcL.left+rcL.right, rcL.top+rcL.bottom);

    return(lstrlen(lpszLocation));
}


// --------------------------------------------------------------------------
//
//  GetObjectShortcut
//
// --------------------------------------------------------------------------
UINT GetObjectShortcut(IAccessible* pacc, VARIANT* pvarChild,
    LPTSTR lpszShortcut, UINT cchShortcut)
{
    HRESULT hr;
    BSTR bstrShortcut;

    *lpszShortcut = 0;
    bstrShortcut = NULL;

    if (!(grfTrack & SEF_USEINVOKE))
    {
        // via IAccessible
        hr = pacc->get_accKeyboardShortcut(*pvarChild, &bstrShortcut);
    }
    else
    {
        DISPPARAMS  dispp;
        EXCEPINFO   excepInfo;
        UINT        errArg;
        VARIANT     varResult;

        // via IDispatch::Invoke
        VariantInit(&varResult);

        dispp.cArgs = 1;
        dispp.cNamedArgs = 0;
        dispp.rgvarg = pvarChild;
        dispp.rgdispidNamedArgs = NULL;

        FillMemory(&excepInfo, sizeof(excepInfo), 0);

        hr = pacc->Invoke(DISPID_ACC_KEYBOARDSHORTCUT, IID_NULL, 0,
            DISPATCH_PROPERTYGET, &dispp, &varResult, &excepInfo, &errArg);
        if (SUCCEEDED(hr) && (varResult.vt == VT_BSTR))
            bstrShortcut = varResult.bstrVal;
        else
            VariantClear(&varResult);
    }
    if (!SUCCEEDED(hr))
        return(0);

    if (bstrShortcut)
    {
        WideCharToMultiByte(CP_ACP, 0, bstrShortcut, -1, lpszShortcut, cchShortcut, NULL, NULL);
        SysFreeString(bstrShortcut);
    }

    return(lstrlen(lpszShortcut));
}

// --------------------------------------------------------------------------
//
//  GetObjectDefAction
//
// --------------------------------------------------------------------------
UINT GetObjectDefAction(IAccessible* pacc, VARIANT* pvarChild,
    LPTSTR lpszDefAction, UINT cchDefAction)
{
    HRESULT hr;
    BSTR bstrDefAction;

    *lpszDefAction = 0;
    bstrDefAction = NULL;

    if (!(grfTrack & SEF_USEINVOKE))
    {
        // via IAccessible
        hr = pacc->get_accDefaultAction(*pvarChild, &bstrDefAction);
    }
    else
    {
        DISPPARAMS  dispp;
        EXCEPINFO   excepInfo;
        UINT        errArg;
        VARIANT     varResult;

        // via IDispatch::Invoke
        VariantInit(&varResult);

        dispp.cArgs = 1;
        dispp.cNamedArgs = 0;
        dispp.rgvarg = pvarChild;
        dispp.rgdispidNamedArgs = NULL;

        FillMemory(&excepInfo, sizeof(excepInfo), 0);

        hr = pacc->Invoke(DISPID_ACC_DEFAULTACTION, IID_NULL, 0,
            DISPATCH_PROPERTYGET, &dispp, &varResult, &excepInfo, &errArg);

        if (SUCCEEDED(hr) && (varResult.vt == VT_BSTR))
            bstrDefAction = varResult.bstrVal;
        else
            VariantClear(&varResult);
    }
    if (!SUCCEEDED(hr))
        return(0);

    if (bstrDefAction)
    {
        WideCharToMultiByte(CP_ACP, 0, bstrDefAction, -1, lpszDefAction,
            cchDefAction, NULL, NULL);
        SysFreeString(bstrDefAction);
    }

    return(lstrlen(lpszDefAction));
}


// --------------------------------------------------------------------------
//
//  GetObjectHelp
//
// --------------------------------------------------------------------------
UINT GetObjectHelp(IAccessible* pacc, VARIANT* pvarChild,
    LPTSTR lpszHelp, UINT cchHelp)
{
    HRESULT hr;
    BSTR bstrHelp;
    LONG idHelpTopic;

    *lpszHelp = 0;
    bstrHelp = NULL;

    if (!(grfTrack & SEF_USEINVOKE))
    {
        // via IAccessible
        hr = pacc->get_accHelp(*pvarChild, &bstrHelp);
    }
    else
    {
        // via IDispatch::Invoke
        DISPPARAMS  dispp;
        EXCEPINFO   excepInfo;
        UINT        errArg;
        VARIANT     varResult;

        // via IDispatch::Invoke
        VariantInit(&varResult);

        dispp.cArgs = 1;
        dispp.cNamedArgs = 0;
        dispp.rgvarg = pvarChild;
        dispp.rgdispidNamedArgs = NULL;

        FillMemory(&excepInfo, sizeof(excepInfo), 0);

        hr = pacc->Invoke(DISPID_ACC_HELP, IID_NULL, 0,
            DISPATCH_PROPERTYGET, &dispp, &varResult, &excepInfo, &errArg);

        if (SUCCEEDED(hr) && (varResult.vt == VT_BSTR))
            bstrHelp = varResult.bstrVal;
        else
            VariantClear(&varResult);
    }
    if (!MYSUCCEEDED(hr))
        return(0);

    if (bstrHelp)
    {
        WideCharToMultiByte(CP_ACP, 0, bstrHelp, -1, lpszHelp,
            cchHelp, NULL, NULL);
        SysFreeString(bstrHelp);
    }
    else
    {
        bstrHelp = NULL;
        idHelpTopic = 0;

        if (!(grfTrack & SEF_USEINVOKE))
        {
            // via IAccessible
            hr = pacc->get_accHelpTopic(&bstrHelp, *pvarChild, &idHelpTopic);
        }
        else
        {
            DISPPARAMS  dispp;
            EXCEPINFO   excepInfo;
            UINT        errArg;
            VARIANT     rgvar[2];
            VARIANT     varResult;

            // via IDispatch::Invoke
            VariantInit(&varResult);

            //
            // NOTE:  the parameters in the DISPPARAMS variant array are in
            // inverse order (right-to-left)
            //
            VariantInit(&rgvar[1]);
            VariantInit(&rgvar[0]);
            VariantCopy(&rgvar[0], pvarChild);

            dispp.cArgs = 2;
            dispp.cNamedArgs = 0;
            dispp.rgvarg = rgvar;
            dispp.rgdispidNamedArgs = NULL;

            FillMemory(&excepInfo, sizeof(excepInfo), 0);

            hr = pacc->Invoke(DISPID_ACC_HELPTOPIC, IID_NULL, 0,
                DISPATCH_PROPERTYGET, &dispp, &varResult, &excepInfo, &errArg);

            if (SUCCEEDED(hr) && (rgvar[1].vt == VT_BSTR) && (varResult.vt == VT_I4))
            {
                bstrHelp = rgvar[1].bstrVal;
                idHelpTopic = varResult.lVal;
            }
            else
            {
                VariantClear(&rgvar[1]);
                VariantClear(&varResult);
            }
        }

        if (SUCCEEDED(hr) && bstrHelp)
        {
            TCHAR szHelpFile[MAX_PATH];

            WideCharToMultiByte(CP_ACP, 0, bstrHelp, -1, szHelpFile,
                MAX_PATH, NULL, NULL);
            SysFreeString(bstrHelp);

            wsprintf(lpszHelp, "Help File: %s, Help Topic: %d.",
                szHelpFile, idHelpTopic);
        }
    }

    return(lstrlen(lpszHelp));
}


// --------------------------------------------------------------------------
//
//  GetObjectParent()
//
//  This gets the name of the parent of an object.
//
// --------------------------------------------------------------------------
UINT GetObjectParent(IAccessible* pacc, VARIANT* pvarChild, LPTSTR lpszParent,
    UINT cchParent)
{
    HRESULT hr;
    IDispatch * pdispParent;
    UINT cchString;
    VARIANT varT;

    *lpszParent = 0;
    pdispParent = NULL;

    VariantInit(&varT);
    VariantCopy(&varT, pvarChild);

    //
    // If the child's ID isn't zero, parent object is the IAccessible* we've 
    // been talking to.  Otherwise, we need to get an IAccessible for that
    // object's parent.
    //
    if (varT.lVal)
    {
        varT.lVal = 0;
        goto GetNameAndRoleOfParent;
    }

    //
    // Get parent object
    //
    pdispParent = NULL;

    if (!(grfTrack & SEF_USEINVOKE))
    {
        // via IAccessible
        hr = pacc->get_accParent(&pdispParent);
    }
    else
    {
        DISPPARAMS  dispp;
        EXCEPINFO   excepInfo;
        UINT        errArg;
        VARIANT     varResult;

        // via IDispatch::Invoke
        VariantInit(&varResult);

        dispp.cArgs = 0;
        dispp.cNamedArgs = 0;
        dispp.rgvarg = NULL;
        dispp.rgdispidNamedArgs = NULL;

        FillMemory(&excepInfo, sizeof(excepInfo), 0);

        hr = pacc->Invoke(DISPID_ACC_PARENT, IID_NULL, 0,
            DISPATCH_PROPERTYGET, &dispp, &varResult, &excepInfo, &errArg);

        if (SUCCEEDED(hr) && (varResult.vt == VT_DISPATCH))
            pdispParent = varResult.pdispVal;
        else
            VariantClear(&varResult);
    }

    if (!SUCCEEDED(hr) || !pdispParent)
    {
        lstrcpy(lpszParent, "screen");
        return(lstrlen(lpszParent));
    }

    //
    // See if it supports OLE Accessibility
    //
    pacc = NULL;
    hr = pdispParent->QueryInterface(IID_IAccessible,
        (void **)&pacc);
    pdispParent->Release();

    if (!SUCCEEDED(hr) || !pacc)
        return(0);

GetNameAndRoleOfParent:
    //
    // Get its name and role
    //
    cchString = GetObjectName(pacc, &varT, lpszParent, cchParent);
    lpszParent += cchString;
    cchParent -= cchString;
    if (cchString)
    {
        *(lpszParent++) = ' ';
        cchString++;
        cchParent--;
    }
    cchString += GetObjectRole(pacc, &varT, lpszParent, cchParent);

    // Did we get a parent object, if so release it
    if (pdispParent)
        pacc->Release();

    return(cchString);
}


/*
    THE INFORMATION AND CODE PROVIDED HEREUNDER (COLLECTIVELY REFERRED TO
    AS "SOFTWARE") IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN
    NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR
    ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL,
    CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF
    MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR
    LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE
    FOREGOING LIMITATION MAY NOT APPLY.
*/
