Exoware ....   Tech Notes    About Exoware    Home
 

Quality solutions, on time, and on budget

 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.

 


Send mail to webmaster@exoware.com with questions or comments about this web site.
Copyright © 1997-2005 Exoware. Last modified: March 16, 2005