You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何判断C++ WinAPI中IWebBrowser2的导航已完成?

Got it, let's work through this problem together. Since you can't rely on MFC or ATL, we'll use raw COM interfaces to check navigation completion safely before calling get_LocationUrl.

Solution Overview

To safely retrieve the current page URL via get_LocationUrl, you need to confirm the browser has finished navigating first. There are two reliable ways to do this with raw COM: checking the browser's ReadyState property, or listening for the DocumentComplete event (the more robust option for most scenarios).

Option 1: Check ReadyState Synchronously

If you just need to poll for navigation completion at a specific point, you can check the READYSTATE enum from the IWebBrowser2 interface. When it returns READYSTATE_COMPLETE, navigation is done and you can call get_LocationUrl.

Here's a code example:

HRESULT GetCurrentUrl(IWebBrowser2* pBrowser, BSTR* pbstrUrl)
{
    if (!pBrowser || !pbstrUrl)
        return E_INVALIDARG;

    READYSTATE readyState;
    HRESULT hr = pBrowser->get_ReadyState(&readyState);
    if (FAILED(hr))
        return hr;

    // Only proceed if navigation is fully complete
    if (readyState != READYSTATE_COMPLETE)
        return E_PENDING; // Return a pending error to indicate navigation isn't done

    // Now safely fetch the URL
    hr = pBrowser->get_LocationUrl(pbstrUrl);
    return hr;
}
Option 2: Listen to DocumentComplete Event (More Reliable)

For real-time detection of when navigation finishes (especially useful for dynamic pages or pages with frames), you can hook into the DWebBrowserEvents2 event interface. The DocumentComplete event fires when the browser finishes loading a document, making it the perfect trigger to call get_LocationUrl.

First, implement the IDispatch interface to handle the event:

class CWebBrowserEventSink : public IDispatch
{
private:
    LONG m_cRefCount;
    IWebBrowser2* m_pBrowser;

public:
    CWebBrowserEventSink(IWebBrowser2* pBrowser) 
        : m_cRefCount(1), m_pBrowser(pBrowser) 
    { 
        if (m_pBrowser) m_pBrowser->AddRef(); 
    }

    ~CWebBrowserEventSink() 
    { 
        if (m_pBrowser) m_pBrowser->Release(); 
    }

    // IUnknown Implementation
    STDMETHOD(QueryInterface)(REFIID riid, void** ppvObj) override
    {
        if (riid == IID_IUnknown || riid == IID_IDispatch || riid == DIID_DWebBrowserEvents2)
        {
            *ppvObj = static_cast<IDispatch*>(this);
            AddRef();
            return S_OK;
        }
        return E_NOINTERFACE;
    }

    STDMETHOD_(ULONG, AddRef)() override 
    { 
        return InterlockedIncrement(&m_cRefCount); 
    }

    STDMETHOD_(ULONG, Release)() override
    {
        ULONG refCount = InterlockedDecrement(&m_cRefCount);
        if (refCount == 0)
            delete this;
        return refCount;
    }

    // IDispatch Implementation
    STDMETHOD(GetTypeInfoCount)(UINT* pCount) override 
    { 
        *pCount = 0; 
        return E_NOTIMPL; 
    }

    STDMETHOD(GetTypeInfo)(UINT, LCID, ITypeInfo**) override 
    { 
        return E_NOTIMPL; 
    }

    STDMETHOD(GetIDsOfNames)(REFIID, LPOLESTR* rgszNames, UINT cNames, LCID, DISPID* rgDispIds) override
    {
        // Map the DocumentComplete event name to its DISPID
        if (cNames == 1 && !wcscmp(rgszNames[0], L"DocumentComplete"))
        {
            *rgDispIds = DISPID_DOCUMENTCOMPLETE;
            return S_OK;
        }
        return DISP_E_UNKNOWNNAME;
    }

    STDMETHOD(Invoke)(DISPID dispId, REFIID, LCID, WORD, DISPPARAMS* pParams, VARIANT*, EXCEPINFO*, UINT*) override
    {
        if (dispId == DISPID_DOCUMENTCOMPLETE)
        {
            // Verify this is the main frame's completion (optional but useful for frames)
            IDispatch* pFrameDisp = pParams->rgvarg[1].pdispVal;
            IWebBrowser2* pFrameBrowser = nullptr;
            HRESULT hr = pFrameDisp->QueryInterface(IID_IWebBrowser2, (void**)&pFrameBrowser);
            if (SUCCEEDED(hr))
            {
                if (pFrameBrowser == m_pBrowser)
                {
                    // Main frame loaded, get the URL
                    BSTR bstrUrl = nullptr;
                    hr = m_pBrowser->get_LocationUrl(&bstrUrl);
                    if (SUCCEEDED(hr) && bstrUrl)
                    {
                        // Do something with the URL (e.g., log it)
                        wprintf(L"Loaded URL: %s\n", bstrUrl);
                        SysFreeString(bstrUrl);
                    }
                }
                pFrameBrowser->Release();
            }
        }
        return S_OK;
    }
};

Then connect the event sink to your browser instance:

HRESULT ConnectBrowserEventSink(IWebBrowser2* pBrowser, DWORD* pdwCookie)
{
    if (!pBrowser || !pdwCookie)
        return E_INVALIDARG;

    IConnectionPointContainer* pCPC = nullptr;
    HRESULT hr = pBrowser->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);
    if (FAILED(hr))
        return hr;

    IConnectionPoint* pCP = nullptr;
    hr = pCPC->FindConnectionPoint(DIID_DWebBrowserEvents2, &pCP);
    pCPC->Release();
    if (FAILED(hr))
        return hr;

    CWebBrowserEventSink* pSink = new CWebBrowserEventSink(pBrowser);
    if (!pSink)
    {
        pCP->Release();
        return E_OUTOFMEMORY;
    }

    hr = pCP->Advise(static_cast<IDispatch*>(pSink), pdwCookie);
    pCP->Release();
    if (FAILED(hr))
    {
        pSink->Release();
        return hr;
    }

    return S_OK;
}
Key Notes
  • Always manage COM interface reference counts properly (use AddRef/Release to avoid memory leaks).
  • If using the event sink, make sure your UI thread has an active message loop—events won't fire without it.
  • For pages with multiple frames, checking if the frame's IWebBrowser2 matches your main browser instance ensures you only act when the entire page finishes loading.

内容的提问来源于stack exchange,提问作者adziri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:35:09