Web浏览器控件 - 使用“写入”显示文本

时间:2013-08-10 09:17:29

标签: internet-explorer webbrowser-control ihtmldocument2

我正在使用IWebBrowser2接口从运行时创建的HTML字符串中呈现页面。我编写了一个方法(让我们称之为DisplayHtmlString),它接受一个HTML字符串并按this example所示呈现它。该方法还首先使用“about:blank”调用Navigate2,以确保文档存在,并在调用write后调用close。

第一次调用DisplayHtmlString时,页面总是正确呈现,即浏览器根据我传递的字符串显示HTML。问题是后续调用有时无法正常工作,而是呈现空白页面。可能导致这种情况的原因是什么?

我发现当显示空白页时,这是导航到about:blank的结果。这是通过导航到本地文件来确定的,然后显示(然后显示HTML字符串,因为后续的写入/关闭)。因此,对Navigate2的调用有效,而写入和关闭的调用有时则不然。

我认为IE内部安全检查是可能的原因(跨域检查?),但我的直觉是,这不是这里发生的事情。

对我而言,似乎更有可能是某种同步问题,“在下一次调用DisplayHtmlString之前IE尚未完成渲染”。我的代码最初没有检查浏览器的就绪状态(因为示例没有)。我添加了一个实验等待循环,调用了get_readyState,并观察到在从方法返回之前状态永远不会超出“加载” - 可能是因为渲染是异步的(?)。我还注意到,当对DisplayHtmlString的连续调用正常工作时,程序的主消息循环已经运行(使Windows有机会处理消息),而在连续调用DisplayHtmlString失败的情况下情况并非如此。

所以我很确定我需要在这里提供正确的同步,但是如何?我注意到有一个名为onreadystatechange的方法,但还没有尝试过,因为我在黑暗中摸索时尝试了很多其他的东西。这可能是解决方案,如何正确使用它?或者,或者,我应该只处理DisplayHtmlString中的消息循环,直到就绪状态变为“完成”?

更新:为DisplayHtmlString添加了消息循环处理。在第一次调用(有效)中,就绪状态变为“交互式”,但没有进一步(这似乎不是问题)。在后续调用中(当它失败时),就绪状态保持在“加载”状态,即使处理了消息循环。

1 个答案:

答案 0 :(得分:3)

您应该在readystatechange对象上处理document事件。在JavaScript中,它看起来像这样:

<body>
<body>Hi, this is going to be replaced!</body>
<script>
window.onload = function()
{
    document.open("text/html");
    document.onreadystatechange = function() { 
        if (document.readyState == "complete")
            alert("Done!"); 
    }
    document.write("<b>Hello again!</b>");
    document.close();   
}
</script>
</body>

要使用C ++或C#完成它,可能最简单的方法是为IDispatch提供IHTMLDocument2::put_readystatechange接口的实现。然后IDispatch::Invoke(DISPID_VALUE)会在readystatechange事件后回复class CEventSink: public CComObjectRoot, public IDispatch { private: HWND m_hwnd; UINT m_message; public: CEventSink() { m_hwnd = NULL; m_message = NULL; } BEGIN_COM_MAP(CEventSink) COM_INTERFACE_ENTRY(IDispatch) END_COM_MAP() // Init void Init(HWND hwnd, UINT message) { m_hwnd = hwnd; m_message = message; } // IDispatch STDMETHODIMP GetTypeInfoCount(UINT* pctinfo) { return E_NOTIMPL; } STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo) { return E_NOTIMPL; } STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid) { return E_NOTIMPL; } STDMETHODIMP Invoke( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pvarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) { if ( dispidMember != NULL ) return DISP_E_MEMBERNOTFOUND; // Just post a message to notify the main window ::PostMessage(m_hwnd, m_message, 0, 0); return S_OK; } };

如果您指定正在使用的语言,我可以帮助您处理代码示例。

<强> [EDITED] 您可以从here获取完整示例(C ++ / ATL / VS2012)。代码通过将自定义消息发布到主窗口来异步执行。这里引用的时间太长了,下面是相关部分。

IDispatch实现,用于onreadystatechange事件接收器:

CComObject<CEventSink>* p = NULL;
hr = CComObject<CEventSink>::CreateInstance(&p);
if ( FAILED(hr) ) 
  return 0;
p->Init(m_hWnd, WM_DOCUMENTREADYSTATECHANGE);
m_eventSink = p; // does AddRef

// ...

m_htmlDocument2->put_onreadystatechange(CComVariant(m_eventSink));

使用它:

WebOcHost.cpp

有关详细信息,请获取the sources并查看{{1}}。为简洁起见,错误检查非常基本。