[英]Webbrowser Control - displaying text using “write”
我正在使用IWebBrowser2接口從運行時創建的HTML字符串呈現頁面。 我已經編寫了一個方法(我們將其稱為DisplayHtmlString),該方法采用一個HTML字符串並將其呈現,如本示例所示。 該方法還首先使用“ about:blank”調用Navigate2,以確保存在文檔,並且在調用write之后也調用close。
第一次調用DisplayHtmlString時,頁面始終呈現正確,即瀏覽器根據我傳遞的字符串顯示HTML。 問題在於,后續調用有時無法正常工作,而是呈現空白頁。 是什么原因造成的?
我發現,當顯示空白頁時,這是導航到about:blank的結果。 這是通過導航到本地文件確定的,然后顯示該文件(由於后續的寫入/關閉操作,因此應顯示HTML字符串)。 因此,對Navigate2的調用有效,而對write和close的調用有時則無效。
我認為IE內部安全檢查是可能的原因(跨域檢查?),但是我的直覺是這不是這里發生的事情。
在我看來,這似乎是某種同步問題,類似“在下一次調用DisplayHtmlString之前,IE還沒有完成渲染”。 我的代碼最初不檢查瀏覽器的就緒狀態(因為該示例未檢查)。 我添加了一個實驗性的等待循環,並調用了get_readyState,並觀察到狀態從方法返回之前從未超出“加載”狀態-可能是因為渲染是異步的(?)。 我還注意到,當連續調用DisplayHtmlString正常工作時,程序的主消息循環已運行(使Windows有機會處理消息),而在連續調用DisplayHtmlString失敗的情況下則不是這種情況。
所以我很確定我需要在此處提供正確的同步,但是如何呢? 我注意到有一個名為onreadystatechange的方法,但是由於我在黑暗中摸索時嘗試了多種其他方法,因此尚未進行過嘗試。 可能是解決方案,如何正確使用呢? 或者,是否應該只在DisplayHtmlString內部處理消息循環,直到就緒狀態變為“完成”?
更新:向DisplayHtmlString添加了消息循環處理。 在第一個調用(有效)中,就緒狀態變為“交互式”,但不再進一步(這似乎不成問題)。 在后續調用中(失敗時),即使處理了消息循環,就緒狀態仍處於“正在加載”狀態。
您應該處理document
對象上的readystatechange
事件。 在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#完成此操作,最簡單的方法可能是為IHTMLDocument2::put_readystatechange
提供IDispatch
接口的實現。 然后,在readystatechange
事件中將IDispatch::Invoke(DISPID_VALUE)
。
如果您指定使用的語言,我也許可以提供代碼示例幫助。
[編輯]您可以從這里獲取完整的示例(C ++ / ATL / VS2012)。 該代碼通過將自定義消息發布到主窗口來異步執行此操作。 這里引用的時間太長了,下面是相關的部分。
IDispatch實現,用於onreadystatechange事件接收器:
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;
}
};
使用它:
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
。 為簡便起見,錯誤檢查是非常基本的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.