如何自动化弹出模态HTML对话框的IE webapp?

时间:2013-04-17 09:29:37

标签: c++ internet-explorer browser-automation iwebbrowser2

[再次修订以获得清晰度]

我有一个与网站互动的C ++程序。该网站是特定于IE的,我的程序也是如此。

我以普通的方式连接到正在运行的IE实例(进程外 - 请参阅代码)。获得IWebBrowser2后,我可以轻松获取IHTMLDocument2并与各个IHTMLElement对象进行互动,填写字段并点击按钮。

但是如果网页上有调用window.showModalDialog的javascript,我就陷入困境:我需要与弹出窗口中的HTML元素进行交互,就像其他页面一样;但我似乎无法得到IWebBrowser2

弹出窗口始终标题为“网页对话框”,是一个Internet Explorer_TridentDlgFrame类型的窗口,其中包含Internet Explorer_Server。但我无法从Internet Explorer_Server窗口获取IWebBrowser2,因为它是一个普通的IE实例。

我可以获得IHTMLDocument2Ptr,但是当我尝试获取IWebBrowser2时,我得到了HRESULT E_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小程序似乎也没有子窗口。)

更新

在评论中,Simon Maurer说上面的代码对他有用,并且只是为了确保没有错别字,他非常慷慨地在pastebin上发布了一个完整的独立应用程序。当我使用他的代码时,它在同一个地方以相同的方式失败,我意识到他想要连接到底层页面,而不是弹出窗口。所以我编辑了上面的文字以消除这种歧义。

1 个答案:

答案 0 :(得分:4)

如果您只想要IServiceProvider,我不知道您为什么要获得IWebBrowser2IHTMLElement。您可以通过调用IHTMLDocument的{​​{1}}方法来获取它们。

此代码段显示了其工作原理:

get_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; } 传递给HWND函数。

警告我在此示例中使用了硬编码的SendMessageTimeout值,如果要对其进行测试,则应启动IE实例并获取HWND的{使用Spy ++的{1}}窗口。

我还建议您使用CComPtr来避免内存泄漏。