Webbrowser Control - display text using "write"

I use the IWebBrowser2 interface to render a page from an HTML string created at runtime. I wrote a method (let it call DisplayHtmlString) that takes an HTML string and displays it, as shown in this example . This method also first calls Navigate2 with "about: blank" to ensure that the document exists, and also closes after calling write.

The first time I call DisplayHtmlString, the page always displays correctly, i.e. The browser displays the HTML according to the line I passed. The problem is that subsequent calls sometimes do not work correctly, but instead create a blank page. What could be the reason for this?

I found out that when a blank page is displayed, this is the result of switching to: blank. This was determined by going to the local file instead, which is then displayed (whereas the HTML line should be displayed instead due to subsequent write / close). Thus, the call to Navigate2 works, and calls to write and close sometimes do not.

I saw IE internal security checks as a possible reason (cross-domain check?), But my gut feeling is that this is not what happens here.

It seems to me more likely that this is some kind of synchronization problem, in accordance with the fact that "IE has not finished showing before the next call to DisplayHtmlString" appears. My code did not initially check the browser’s ready state (because the example doesn’t). I added an experimental wait loop with a call to get_readyState and noticed that the state never went beyond the "load" before returning from the method - perhaps because the rendering is asynchronous (?). I also notice that when successive DisplayHtmlString calls work correctly, the program’s main message loop starts (which gives Windows the ability to process messages), which is not the case when successive call failures in DisplayHtmlString do not work.

So, I am sure that I need to ensure the correct synchronization here, but how? I notice there a method called onreadystatechange, but have not experimented with it yet due to many other things that I tried when I felt for the darkness. Could this be a solution, and how to use it correctly? Or, alternatively, should I process the message loop inside the DisplayHtmlString until the ready state changes to "completed"?

UPDATE: added message loop processing in DisplayHtmlString. In the first call (which works), the ready state goes into "interactive", but no further (which does not seem to be a problem). In a subsequent call (when it fails), the ready state remains at boot, even if the message loop is being processed.

+1
source share
1 answer

You must handle the readystatechange event on the document object. In JavaScript, it will look like this:

 <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> 

To do this using C ++ or C #, perhaps the easiest way would be to provide an IDispatch interface implementation for IHTMLDocument2::put_readystatechange . IDispatch::Invoke(DISPID_VALUE) will then be called on the readystatechange event.

I could help with sample code if you specify the language used.

[EDITED] You can take the full example (C ++ / ATL / VS2012) from here . The code does this asynchronously, sending a special message to the main window. It is too long to quote here, below are the relevant parts.

implementation of IDispatch, for onreadystatechange Event Sink:

 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; } }; 

Using it:

 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)); 

See sources for more details and see WebOcHost.cpp . Error checking is very simple, for short.

+2
source

All Articles