我正在使用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添加了消息循环处理。在第一次调用(有效)中,就绪状态变为“交互式”,但没有进一步(这似乎不是问题)。在后续调用中(当它失败时),就绪状态保持在“加载”状态,即使处理了消息循环。
答案 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}}。为简洁起见,错误检查非常基本。