11

[为清楚起见再次修订]

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

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

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

弹出窗口总是标题为“网页对话框”,并且是一个Internet Explorer_TridentDlgFrame包含Internet Explorer_Server. Internet Explorer_Server但是当它是一个普通的 IE 实例时,我无法从窗口中获取 IWebBrowser2 。

我可以得到IHTMLDocument2Ptr,但是当我尝试得到时,IWebBrowser2我得到了HRESULTof 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
         }
    }
}

万一重要,这是VistaIE8。(我强调这一点是因为这两个都在我的代码库中引入了重大更改,这些更改在 XP/IE7 上运行良好。)

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

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

编辑添加:

有人建议获取子窗口并向它们发送消息,但我很确定这不适用于Internet Explorer_Server; 根据 Spy++,没有任何子窗口。(这不是 IE 特定的。Java 小程序似乎也没有子窗口。)

更新

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

4

1 回答 1

4

我不知道你为什么想要得到IServiceProvider或者IWebBrowser2你只是想要IHTMLElement's。您可以通过调用IHTMLDocument'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;
}

上面的代码适用于两者:普通窗口或模态窗口,只需将正确的传递HWNDSendMessageTimeout函数。

警告我在这个例子中使用了一个硬编码的HWND值,如果你想测试它,你应该启动一个 IE 实例并使用 Spy++ 获取HWND窗口Internet Explorer_Server

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

于 2013-04-25T12:11:58.210 回答