如何自动化弹出模态HTML对话框的IE web应用



[再次修改为清楚起见]

我有一个与网站交互的c++程序。该站点是特定于ie的,我的程序也是。

我以一种普通的方式连接到IE的运行实例(退出进程——见代码)。一旦我得到了IWebBrowser2,我就可以毫无问题地得到IHTMLDocument2,并与单个IHTMLElement对象进行交互,填写字段并单击按钮。

但是如果网页有javascript调用window。showModalDialog,我卡住了:我需要与弹出框中的HTML元素进行交互,就像其他页面一样;但我似乎无法理解它的IWebBrowser2

这个弹出窗口总是标题为"网页对话框",并且是一个包含Internet Explorer_ServerInternet Explorer_TridentDlgFrame类型的窗口。但我不能得到IWebBrowser2从Internet Explorer_Server窗口的方式,我可以当它是一个正常的IE实例。

我可以得到IHTMLDocument2Ptr,但是当我试图得到IWebBrowser2时,我得到的是E_NOINTERFACEHRESULT

代码是相当标准的东西,并工作良好,如果它是一个"正常"的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
         }
    }
}

如果重要的话,这是VistaIE8。(我之所以强调这一点,是因为这两种方法都给我的代码库带来了破坏性的变化,而我的代码库在XP/IE7上运行得很好。)

再一次,我的目标是获得每个IHTMLElement并与之交互。我无法访问我正在自动化的应用程序的源代码。

我正在考虑将按键盲目地发送到Internet Explorer_Server窗口,但宁愿不。

编辑添加:

有人建议获得子窗口并向他们发送消息,但我很确定这与Internet Explorer_Server不起作用;根据spy++,没有任何子窗口。(这不是特定于ie的。Java小程序似乎也没有子窗口。)

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

如果你只想要IHTMLElement,我不知道为什么你想要IServiceProviderIWebBrowser2,你可以通过调用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: %wsn", 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("%wsn", 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实例,并使用spy++获得Internet Explorer_Server窗口的HWND

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

相关内容

  • 没有找到相关文章

最新更新