Setting HTML view text directly from a string
| Exoware ..... | Tech Note |
| Setting HTML View Text Directly From a String | Eric Hartwell, September 1998 |
The problem is that an HTML browser is designed to display web pages from URLs, not from dynamically generated text. With Microsoft's MSHTML browser , you can:
- Create a named temporary file, write your text to it, pass the file path to the browser's Navigate method, then delete the temporary file AFTER navigation is complete.
- Reuse the same temporary file each time ( Make sure there's only one user).
- Write your own "Pluggable Protocol" handler to support your own pseudo-URLs (e.g. "res:").
- Write some HTML script that takes a block of text and loads it into the document
- Use the IHTMLDocument2 interface to write directly to the document.
This example writes directly to the document using the poorly documented open(), write(), and close() methods of the IE Document Object Model. There's a catch - the document object isn't valid until after the browser has finished navigating to the first page. So, before you can do anything, you have to navigate to a page.
- A good page to navigate to is Internet Explorer's internal "about:Blank". (A bad page is "about:NavigationCanceled")
- You might put the Navigate("about:Blank") command in the view's OnInitialUpdate() method. I prefer to defer the screen update until you're actually ready to output - this will minimize the time the user has to look at a blank screen.
- You have to wait until the first OnNavigateComplete() event before you can use the document object. Since the browser operates asynchronously, set a state flag and buffer the HTML text for the first screen.
- For subsequent pages, you can use the Stop() method to cancel any pending navigation or download operations, then load your HTML text immediately.
void CMyHtmlObjCommView::NavigateText(const char *pszText)
{
// The Document Object Model isn't fully initialized until
// the browser has finished navigating to a URL. A quick
// and handy one is "about:blank"
if (!m_bHasDocument) // See if this is the first time in
{
m_strFirstText = pszText; // Save the text for later
Navigate("about:blank"); // Initialize system to a blank screen
return;
}
// The DOM is now valid. Use the open() and write() methods
// to set the entire screen's HTML.
if (GetBusy()) // See if it's busy with something else
Stop(); // .. and make sure it's not
Now that the document is ready, you can set its HTML contents using the write() method.
- Get a pointer to the browser's IHTMLDocument2 interface
- Use the open() method to open a stream on the document
- Unfortunately, the write() method requires the text to be stored as a SAFEARRAY of BSTRs. This means it's next to impossible to use in Visual Basic, and a real pain in C++.
Use the close() method to close the output stream and send the data to the display.
// Get a pointer to the HTML Document Object Model's interface
IDispatch * pDisp = GetHtmlDocument();
if (!pDisp)
return;
IHTMLDocument2* pDoc;
if (SUCCEEDED(pDisp->QueryInterface( IID_IHTMLDocument2, (void**)&pDoc )))
{
// Empty URL and parameters opens the current document
CComBSTR bstrURL;
CComVariant varDummy;
pDoc->open(bstrURL, varDummy, varDummy, varDummy, NULL);
// Create a safearray to store the HTML text
SAFEARRAY *pSA;
SAFEARRAYBOUND saBound = {1, 0};
pSA = SafeArrayCreate(VT_VARIANT, 1, &saBound);
// Copy the HTML into the one and only element
VARIANT *pVar;
CComBSTR bstrHTML = pszText; // Load the text
varDummy = bstrHTML; // .. into a variant
SafeArrayAccessData(pSA, (void**)&pVar); // Access safearray data
pVar[0] = varDummy; // Set the text data
SafeArrayUnaccessData(pSA); // Release access
// Write the HTML as the document's new text
pDoc->write(pSA); // Overwrite HTML
pDoc->close(); // Update browser
SafeArrayDestroy(pSA); // Finished with the safearray
pDoc->Release(); // Don't forget to release the reference
}
The first time through, the document may not have been initialized, so we stored the text in the m_strFirstText variable. The browser fires a NavigateComplete event when the initial document is loaded, so we know it's OK to send the text.
void CMyHtmlObjCommView::OnNavigateComplete2(LPCTSTR strURL)
{
CHtmlView::OnNavigateComplete2(strURL);
if (!m_bHasDocument) // First time
{
m_bHasDocument = true; // DOM is now valid
if (!m_strFirstText.IsEmpty()) // Was there a deferred load?
{
NavigateText(m_strFirstText); // Navigate now
m_strFirstText.Empty(); // Done.
}
}
}
Resources
- MSDN, Microsoft Knowledge Base, Site Builder Workshop, Platform SDK, etc. I finally found out how to get the SAFEARRAY code to work in Professional ATL COM Programming, Wrox Press Ltd, by Dr. Richard Grimes.