Microsoft has a WebBrowser control that is actually an Internet Explorer control that you can use to display HTML in your own applications. More information about this WebBrowser control can be found on MSDN. By using this control it’s very easy to display online or offline webpages. However, it’s not immediately obvious how to make it display HTML that you might have in a memory buffer. Of course, one solution is to write the HTML to a temporary file and then load that file using the WebBrowser control, but obviously there is a better way for doing this which I will explain below.
The first thing you have to do is to add the WebBrowser control to your dialog. So, in Visual Studio, open the resource editor and then open the dialog onto which you want to put the WebBrowser control. Once the dialog is opened in the resource editor, right click on an empty space on the dialog and select “Insert ActiveX Control…”. This will open a new window in which you can select “Microsoft Web Browser” and then click OK. Visual Studio will automatically create a wrapper class for this ActiveX control which will probably be called explorer.h and explorer.cpp while the wrapper class will most likely be called CExplorer.
Now, right click the WebBrowser control on your dialog and select “Add Variable”. Make a variable with category set to “Control” and with the variable type set to the wrapper class “CExplorer” and hit OK.
Now we can start writing code. The first thing required is to load up some basic document; I use about:blank. Do this in your OnInitDialog handler as follows.
COleVariant loc(L"about:blank");
m_explorer.Navigate2(loc, NULL, NULL, NULL, NULL);
The above is very important. If you don’t load an initial document, the WebBrowser control will not render any HTML that you try to push to it. This also means that before you can start writing HTML from memory in the WebBrowser control, you have to wait until the initial document has been fully loaded. This can be done with the “DocumentComplete” event. In the dialog editor, right the WebBrowser control and click on “Add Event Handler…”. Select “DocumentComplete” as message type, select the appropriate class and click “Add and Edit”. You can use that handler to change a boolean variable in your code to mark whether the document has been fully loaded. When it is fully loaded you can start writing HTML from memory to it.
Once that is finished, you can add the following helper function:
#include <MsHTML.h>
void CMyDlg::WriteHTML(const wchar_t* html)
{
IDispatch* pHtmlDoc = m_explorer.get_Document();
if (!pHtmlDoc)
return;
CComPtr<IHTMLDocument2> doc2;
doc2.Attach((IHTMLDocument2*)pHtmlDoc);
if (!doc2)
return;
// Creates a new one-dimensional array
SAFEARRAY* psaStrings = SafeArrayCreateVector(VT_VARIANT, 0, 1);
if (!psaStrings)
return;
BSTR bstr = SysAllocString(html);
if (bstr)
{
VARIANT* param;
HRESULT hr = SafeArrayAccessData(psaStrings, (LPVOID*)¶m);
if (SUCCEEDED(hr))
{
param->vt = VT_BSTR;
param->bstrVal = bstr;
hr = SafeArrayUnaccessData(psaStrings);
if (SUCCEEDED(hr))
{
doc2->write(psaStrings);
doc2->close();
}
}
}
// SafeArrayDestroy calls SysFreeString for each BSTR!
if (psaStrings)
SafeArrayDestroy(psaStrings);
}
With the above function, it’s very easy to dynamically create and display HTML from memory. For example:
WriteHTML(L"<html><body><h1>My Header</h1><p>Some text below the header</p></body></html>");
Note that the above code is expecting a Unicode build. If you don’t use Unicode, you need to change the wchar_t types and you need to change the way how you allocate the BSTR variable.
That’s it. Pretty easy if you know how to do it, but it took me some time to figure it out.
[ Update: Fixed some typos and added mshtml.h reference. ]
[ Update 2: Added a call to “doc2->close();” after “doc2->write()” and added code to check the result of the doc2.Attach() call. ]