2013-04-17 53 views
11

[再次尚未修訂爲清晰起見]如何自動化彈出模態HTML對話框的IE Web應用程序?

我有一個C++程序,它與網站交互。該網站是特定於IE的,我的程序也是如此。

我以普通方式連接到IE的運行實例(超出處理範圍 - 請參閱代碼)。一旦我得到IWebBrowser2,我沒有問題得到IHTMLDocument2和與個人IHTMLElement對象交互,填寫字段和單擊按鈕。

但是,如果網頁有JavaScript,調用window.showModalDialog,我卡住了:我需要與彈出窗口中的HTML元素進行交互,就像其他頁面一樣;但我似乎無法得到其IWebBrowser2

彈出總是名爲「網頁對話框」,並且是包含Internet Explorer_ServerInternet Explorer_TridentDlgFrame的一個窗口。但是我無法從我的方式從Internet Explorer_Server窗口獲取IWebBrowser2,這是一個普通的IE實例。

我可以得到IHTMLDocument2Ptr,但是當我嘗試獲得IWebBrowser2時,我得到一個HRESULTE_NOINTERFACE

的代碼是非常標準的東西,如果它是一個「正常」的IE窗口

IHTMLDocument2Ptr pDoc; 
LRESULT lRes; 

/* hWndChild is an instance of class "Internet Explorer_Server" */ 

UINT nMsg = ::RegisterWindowMessage("WM_HTML_GETOBJECT"); 
::SendMessageTimeout(hWndChild, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, 
    (DWORD*)&lRes); 

LPFNOBJECTFROMLRESULT pfObjectFromLresult = 
    (LPFNOBJECTFROMLRESULT)::GetProcAddress(hInst, "ObjectFromLresult"); 
if (pfObjectFromLresult != NULL) 
{ 
    HRESULT hr; 
    hr = (*pfObjectFromLresult)(lRes, IID_IHTMLDocument, 0, (void**)&pDoc); 
    if (SUCCEEDED(hr)) { 
     IServiceProvider *pService; 
     hr = pDoc->QueryInterface(IID_IServiceProvider, (void **) &pService); 
     if (SUCCEEDED(hr)) 
     { 
      hr = pService->QueryService(SID_SWebBrowserApp, 
       IID_IWebBrowser2, (void **) &pBrowser); 

      // This is where the problem occurs: 
      // hr == E_NOINTERFACE 
     } 
    } 
} 

在它的問題的情況下正常工作,這是Vista的IE8。 (我強調這一點,因爲這兩個引入了我的代碼庫中的突破性變化,這在XP/IE7中運行良好。)

再次,我的目標是獲得每個IHTMLElement並與它交互。我無法訪問自動化的應用程序的源代碼。

我在考慮盲目發送按鍵到Internet Explorer_Server窗口,但寧願不要。

編輯補充:

有人建議讓子窗口和發送他們的消息,但我敢肯定,不Internet Explorer_Server工作;根據Spy ++,沒有任何子窗口。 (這不是IE特有的。Java小程序似乎並不有子窗口,無論是。)

更新

在評論中,西蒙毛雷爾說,上面的代碼爲他工作,而只是爲了確保沒有錯別字,他非常慷慨地在pastebin上發佈了一個完整的獨立應用程序。當我使用他的代碼時,它在同一個地方以相同的方式失敗,我意識到他以爲我想連接底層頁面,而不是彈出窗口。所以我編輯了上面的文字以消除這種模糊性。

+0

什麼是例外?調用'pDoc-> QueryInterface'時'pDoc'看起來有效嗎? –

+0

@NateHekman:我已經大幅修改了這個問題。 – egrunin

+0

你能確認C++應用程序是否在進程外?什麼是「網頁對話框」?是腳本調用showModalDialog時彈出的IE窗口? –

回答

3

我不知道你爲什麼想要IServiceProviderIWebBrowser2如果你只想要IHTMLElement的。你可以通過調用IHTMLDocumentget_all()方法來獲得它們。

此代碼片段顯示了這是如何工作:

#include <Windows.h> 
#include <mshtml.h> 
#include <Exdisp.h> 
#include <atlbase.h> 
#include <SHLGUID.h> 
#include <oleacc.h> 
#include <comdef.h> 
#include <tchar.h> 

HRESULT EnumElements(HINSTANCE hOleAccInst, HWND child) 
{ 
    HRESULT hr; 

    UINT nMsg = ::RegisterWindowMessage(_T("WM_HTML_GETOBJECT")); 
    LRESULT lRes = 0; 
    ::SendMessageTimeout(child, nMsg, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (PDWORD)&lRes); 

    LPFNOBJECTFROMLRESULT pfObjectFromLresult = (LPFNOBJECTFROMLRESULT)::GetProcAddress(hOleAccInst, "ObjectFromLresult"); 
    if (pfObjectFromLresult == NULL) 
     return S_FALSE; 

    CComPtr<IHTMLDocument2> spDoc; 
    hr = (*pfObjectFromLresult)(lRes, IID_IHTMLDocument2, 0, (void**)&spDoc); 
    if (FAILED(hr)) return hr; 

    CComPtr<IHTMLElementCollection> spElementCollection; 
    hr = spDoc->get_all(&spElementCollection); 
    if (FAILED(hr)) return hr; 

    CComBSTR url; 
    spDoc->get_URL(&url); 
    printf("URL: %ws\n", url); 

    long lElementCount; 
    hr = spElementCollection->get_length(&lElementCount); 
    if (FAILED(hr)) return hr; 
    printf("Number of elements: %d", lElementCount); 

    VARIANT vIndex; vIndex.vt = VT_I4; 
    VARIANT vSubIndex; vSubIndex.vt = VT_I4; vSubIndex.lVal = 0; 
    for (vIndex.lVal = 0; vIndex.lVal < lElementCount; vIndex.lVal++) 
    { 
     CComPtr<IDispatch> spDispatchElement; 
     if (FAILED(spElementCollection->item(vIndex, vSubIndex, &spDispatchElement))) 
      continue; 
     CComPtr<IHTMLElement> spElement; 
     if (FAILED(spDispatchElement->QueryInterface(IID_IHTMLElement, (void**)&spElement))) 
      continue; 
     CComBSTR tagName; 
     if (SUCCEEDED(spElement->get_tagName(&tagName))) 
     { 
      printf("%ws\n", tagName); 
     } 
    } 
    return S_OK; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    ::CoInitialize(NULL); 
    HINSTANCE hInst = ::LoadLibrary(_T("OLEACC.DLL")); 
    if (hInst != NULL) 
    { 
     HRESULT hr = EnumElements(hInst, (HWND)0x000F05E4); // Handle to Internet Explorer_Server determined with Spy++ :) 
     ::FreeLibrary(hInst); 
    } 
    ::CoUninitialize(); 
    return 0; 
} 

上面的代碼工作兩個:一個普通的窗口或模式窗口,只需傳遞正確的HWNDSendMessageTimeout功能。

警告我在這個例子中使用硬編碼HWND值,如果你想測試你應該開始一個IE實例,並得到使用間諜++的Internet Explorer_Server窗口HWND

我還建議您使用CComPtr以避免內存泄漏。

+0

我會試試這個。您使用的是哪個版本的IE? – egrunin

+0

Windows 7 x64上的IE10 –

+0

Windows XP 32bit上的IE8也能正常工作 –

相關問題