2013-04-17 20 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也能正常工作 –

相关问题