如何在MFC中获取嵌入式Web浏览器控件的HWND

4

我正在使用嵌入式Web 浏览器控件在我的基于对话框的 MFC 窗口中,我需要知道其中 Web 浏览器控件的 HWND 。我找到了以下代码,它声称可以检索它:

HWND hWndWebBrowser = NULL;

LPUNKNOWN unknown = m_browser.GetControlUnknown();

IWebBrowser2* pWB = NULL;
if(SUCCEEDED(unknown->QueryInterface(IID_IWebBrowser2,(void **)&pWB)))
{
    CComPtr<IServiceProvider> pServiceProvider;
    if (SUCCEEDED(pWB->QueryInterface(IID_IServiceProvider, (void**)&pServiceProvider)))
    {
        CComPtr<IOleWindow> pWindow;
        if (SUCCEEDED(pServiceProvider->QueryService(SID_SShellBrowser, IID_IOleWindow, (void**)&pWindow)))
        {
            SHANDLE_PTR hBrowser = 0;
            if (SUCCEEDED(pWindow->GetWindow(&hBrowser)))
            {
                hWndWebBrowser = (HWND)hBrowser;
            }
        }
    }
}

if(unknown)
{
    unknown->Release();
}

问题在于,当它运行时返回的句柄不是我所期望的句柄。最好的方法是使用这个 Spy++ 屏幕截图说明:

enter image description here

我知道可以使用EnumChildWindows查找一个带有 Internet Explorer_Server 类名的窗口,但我对使用这个未记录的类名有些担忧。

有没有更好的方法来检索那个(Web浏览器)窗口句柄呢?


听起来有点像 XY 问题。你为什么需要那个特定的 HWND?它有什么特别之处?因为这可能是你答案的关键。 - MSalters
你担心使用类名?但是一旦你有了窗口句柄,你应该对你如何处理这个窗口句柄有完全相同的担忧。你打算用它做什么? - David Heffernan
1
@DavidHeffernan:那个句柄有多种合法用途。这是我脑海中的一个例子。我需要知道浏览器控件是否具有键盘焦点(结果将影响弹出对话框中的选择),因此我执行 bool bHadFocus = ::GetFocus() == hIEWnd; 但为了使其正常工作,我需要知道 hIEWnd - c00000fd
不是我的观点。我的观点是,如果您关心跨版本的健壮性,即使找到了窗口句柄,这些问题仍然存在。 - David Heffernan
@DavidHeffernan:是的,我听到你说的了,但这种情况发生的可能性较小。我现在担心的是微软可能会逐步淘汰IE,因此该控件可能会有一个新的类名,例如Spartan_Server或类似的名称。 - c00000fd
3个回答

2
根据获取WebBrowser控件HWND的说明,您可以使用以下函数来检索HWND。
IOleWindow *pOWin;
HWND hBWnd;

HRESULT hRes = m_pBrowserApp->QueryInterface(IID_IOleWindow, (void **)&pOWin);
if (SUCCEEDED(hRes)) {
    hRes = pOWin->GetWindow(&hBWnd);
    if (SUCCEEDED(hRes)) {
        // Place hBWnd-manipulating code here
    }
pOWin->Release(); // Missing from the MS example
}   

由于类名(Shell DocObject ViewInternet Explorer_Server)可能会更改,因此上述代码应该是首选,尽管这不太可能发生,因为Internet Explorer现在已经停止使用。


对不起,但它给我与我上面的代码完全相同的结果。 - c00000fd
@c00000fd 你是否添加了 poWin->Release(); - Coder12345

1
问题的词汇有些棘手。
(Web浏览器)的HWND确实是您发布的答案和Santosh Dhanawade发布的答案。
当文档加载时,Web浏览器控件会创建一个新窗口或iframe,请参阅DWebBrowserEvents2 :: DocumentComplete事件。
事件处理程序参数:
pDisp [in] ”

指向加载文档的窗口或帧的IDispatch接口的指针。可以查询此IDispatch接口以获取IWebBrowser2接口。

因此,更改问题:
“是否有人有更好的方法检索(Web浏览器)窗口句柄?”
到:
“是否有人有更好的方法检索(窗口或iframe)窗口句柄?”
我们知道,您正在寻找的窗口或iframe HWND将在文档完成加载后可用。
这意味着我们可以执行以下操作:

通过使用IDispatch的原始c或c++实现,或者ATL DispEventImpl或ATL DispEventSimpleImpl,实现DocumentComplete事件处理程序。请参见理解COM事件处理

将事件处理程序连接到Web浏览器控件中,以获取事件报告。

从DocumentComplete事件中获取窗口或iframe HWND:

假设使用原始c++ IDispatch实现:

IFACEMETHODIMP DWebBrowserEvents2Impl::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pDispParams, VARIANT * pVarResult, EXCEPINFO * pExcepInfo, UINT * puArgErr)
{
    if (dispIdMember == DISPID_DOCUMENTCOMPLETE) {
        VARIANT variantDispatch;
        VariantInit(&variantDispatch);
        HRESULT hr = DispGetParam(pDispParams, 0, VT_DISPATCH, &variantDispatch, NULL);
        if (SUCCEEDED(hr)) {            
            IOleWindow* iOleWindow;
            hr = variantDispatch.pdispVal->QueryInterface(IID_IOleWindow, (LPVOID*) &iOleWindow);
            if (SUCCEEDED(hr)) {
                HWND hwnd;
                hr = iOleWindow->GetWindow(&hwnd);
                iOleWindow->Release();
                if (SUCCEEED(hr)){
                //now the hwnd correponds to the Internet Explorer_Server window.
                //Do what ever you want with the HWND handler.              
                }  
            }           
        }
        return S_OK;
    }
    return E_NOTIMPL;
}

0

根据我的经验,我们要找的窗口是CHtmlView派生的CWnd的直接后代,因此我使用这个技巧来获取窗口并将焦点设置到它上面:

static CWnd* findChildWebbrowser(CWnd* pWnd) {
    if(pWnd == NULL) { return NULL; }
    CWnd* pC = pWnd->GetWindow(GW_CHILD);
    if(pC == NULL) { return NULL; };
    CString buf;
    ::GetClassName(pC->GetSafeHwnd(), buf.GetBuffer(2048), 2047);
    buf.ReleaseBuffer();
    if(buf == _T("Internet Explorer_Server")) {
        return pC;
    }
    return findChildWebbrowser(pC);
}

void CMyWebView::OnSetFocus(CWnd* pOldWnd) {
    // CHtmlView::OnSetFocus(pOldWnd);
    CWnd* pIE = findChildWebbrowser(this);
    if(pIE!=NULL) {
        // this makes cursor/page keys work
        pIE->SetFocus();
        // this makes the TAB key work
        pIE->SendMessage(WM_LBUTTONDOWN);
        pIE->SendMessage(WM_LBUTTONUP);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接