MSHTML Hosting – Building UI’s

My last posts have dealt with using the WebBrowser component to display HTML pages inside your application. We have seen how it does not take much to embed the control to create your own little mini-webbrowser. In this post I want to go a little further than building a webbrowser. Lots of applications are using a web-like UI. Applications like Intuit Quicken, Microsoft Outlook and Money actually use the WebBrowser control to achieve their web-like UI’s. Microsoft Office task panes and other inductive UI’s can be developed using the WebBrowser control.

When using the WebBrowser control for building a UI there are a few issues to consider:

  1. Loading the HTML into the control. You most likely will not be using Navigate to load the HTML. In most cases you are dynamically generating the HTML from scratch or from a template loaded from a resource.
  2. Handling mouse and keyboard events. Since you are building a UI, it is very likely that the content will be interactive. There may be edit boxes, push buttons and hyperlinks in the content. You will need to handle those events and respond accordingly.

Loading HTML

The most common (but not the only) way to load HTML from a buffer to the WebBrowser is via streams. Microsoft has a nice example in the WebBrowser reference. A quick Google search will turn up a way to do it in your favorite tool or language. The key points here are:

  1. Navigate to “about:blank”
  2. Wait for the blank document to finish loading
  3. Load your HTML using the IPersistStreamInit method

Step #3 looks like this:


IHTMLDocument2* pDoc = ...;
IStream* pMyStream = ...;

IPersistStreamInit* pPersist = 0;
HRESULT hr = pDoc->QueryInterface(IID_IPersistStreamInit, (void**)&pPersist);
if (SUCCEEDED(hr) && pPersist) {
    hr = pPersist->InitNew();
    if (SUCCEEDED(hr)) {
        hr = pPersist->Load(pMyStream);
    }
    pPersist->Release();
}

Note: I would strongly recommend that any external links you have in your generated HTML (stylesheets or JavaScript) are referenced using absolute paths. Do not use relative paths. The IPersistStreamInit method does not update the control’s base URL. Navigate does update the base URL. Therefore, any relative path will have “about:blank” prepended to it and the control will not likely find your external link.

Handling Events

The ability to handle mouse and keyboard events is critical when creating an interactive UI. There are 2 basic methods to do this with WebBrowser:

  1. Use <A> tags to create hyperlinks with bogus href URL’s. Then use OnBeforeNavigate2 to intercept the bogus URL, cancel the navigation and respond to the mouse click.
  2. Hook your native code to the onclick, onkeypress, or any of the many other JavaScript events.

Method #1 is cheap and easy, but really only works with hyperlinks. In my applications, I create bogus hyperlinks that are easy to parse inside OnBeforeNavigate2 and contain breadcrumbs for me to use when responding to the click. Here is an example:

myapp://edititem/12345

I can look for a constant substring to indicate its my bogus HREF (myapp://). I can figure out the type of action (edititem). I also know which item to edit (12345). The last part could be a database ID or a pointer to an object cast to a long integer. The whole thing even looks like a real URL too.

Method #2 is much more robust, but a little more complicated to implement. There are more steps involved. You will also be working with the IHTMLDocument system and creating IDispatch wrappers. We’ll cover those topics in future posts.

MSHTML Hosting – The Basics

Hosting IE in your application is a relatively straight forward process, provided your development environment supports the use of ActiveX controls. Each language/framework has its own way of doing it: VB works directly with the WebBrowser control, MFC has its CHtmlView wrapper classes, Delphi has the TWebBrowser wrapper and C++Builder uses TCppWebBrowser. Create one of these somewhere in your application and your on your way to displaying HTML pages.

Before I go any further, I want to point you to the MSDN documentation of reusing the WebBrowser control. It will be an invaluable reference to you.

The WebBrowser control is really made up of a command interface (IWebBrowser2) and event interfaces (DWebBrowserEvents and DWebBrowserEvents2). Unless you are writing code against the raw control (please don’t), your wrapper component will expose both of these sides to you automatically. The event names may be slightly different between components. Here are the most useful methods and events:

  • Document – This property is your means to gain access to the IHTMLDocument2 MSHTML interface. More on this in later posts, I just wanted to point it out now.
  • Navigate / Navigate2 – Provides a simple way to tell the WebBrowser to display a page from a given file or URL. Remember to specify the full URL (including http://). Navigate is the simpler method. Both support functionality such as passing in flags to keep the page from displaying in IE’s cache list.
  • GoHome / GoBack / GoForward / Refresh – Allow you to mimic the IE functionality with the respective names.
  • ExecWB – Provides a way to get the WebBrowser to execute commands (listed here), such as Print, Print Preview, Save As, Copy and Find.
  • OnBeforeNavigate2 – Event that is called before the WebBrowser actually navigates to a given page. This event allows you to cancel or redirect the navigation. Many embedded browser applications use this event to implement “custom protocols” where clicking on a link will display your dialog, for example.
  • OnDocumentComplete – Event that is called when a page is fully loaded into the browser. Use this event as a trigger for hooking up other functionality that can only be done after a page is completely in the browser.
  • OnNavigateComplete2 – Event that is fired as individual pieces of the page are loaded. Many people assume this event will only be called once per page load. Not true, it is called once for each frame and then for the page. It is usually safer to use OnDocumentComplete, unless you need to be notified for each frame.

Using these methods and events, it is very easy to create a nicely featured web browser. Next time we can look at ways to make WebBrowser seem less like a web browser and more like a custom HTML display control you can use inside your application.

Working With MSHTML Hosting

On the surface it seems like a great deal. You can actually embed MSHTML, the IE HTML rendering engine, in your own application. There is a lot of cool, simple features you get out-of-the-box. As soon as you get more advanced in your features, you find things are not so simple.

First, lets clear up some terminology:

  • WebBrowser – is an ActiveX control that you can embed in your applications to create a mini webbrowser. It will display HTML pages just as well as IE itself.
  • MSHTML – is a set of COM interfaces that you can use to programmatically access the elements of an HTML page. The interfaces also allow you to take part in Dynamic HTML events as well as behind the scenes operations like editing, custom rendering and behaviors, and selection.

WebBrowser depends on MSHTML. In fact there is not much beyond navigating to an HTML page that you can do with WebBrowser alone.

Myself and my team have become quite familiar with the ins and outs of MSHTML hosting. Never have I seen a more clear case of the 80/20 rule. MSHTML will get you 80% of your features very quickly and with relative ease. That last 20% will break most of you.

By no means do I consider myself an expert on MSHTML hosting, but I have implemented some tough features. One of the hardest things about moving past the beginner level stuff is the lack of real examples. I thought I would collect some links to stuff I found useful and post some code examples as well.

More to come

Mini-Milestones Can’t Slip

I am a big proponent of the staged delivery concept of software development. McConnell’s treatment really brought it home for me when I first read Software Survival Guide. The recent Agile methods also preach the same ideas. It’s just good sense: Always have a buildable, releasable product.

The reason for this post is not to act as a cheerleader for staged delivery. It’s to vent-off some steam. At work, we have created a system of mini-milestones we are using to implement staged delivery. The other day we decide to slip a mini-milestone! Feature-creep /scope-creep caused us to miss a milestone!

I lost it. These milestones aren’t for creating releases (we are far from that). The milestones act as checkpoints during the development process. The fact that we slipped a mini-milestone tells me that our process needs to be examined. The fact that we slipped is a red flag to me. If we can’t contain scope-creep now, at this early point in the process, there is no way we can contain it later.

More on Task-based UI’s

Microsoft published the first article of a two-part series on Inductive UI (IUI) design, their buzzword for Task-based UI. This one covers a couple things:

  • How IUI can help users get frequent tasks completed faster.
  • What is a frequent task.
  • How you can implement a IUI design using a .NET library.

If you have ever seen an IUI design (think Task Panes in MS Office 2003), you will almost immediately see Web-style similarities. The article discusses this, referring to Dialog-style versus Web-style UI’s. The author does note that in many cases experienced users will prefer Dialog-style UI’s over Web-style. While I can agree with the sentiment, it usually happens in cases where the Web-style UI is designed to perform a long, drawn out wizard process. In most cases, such Task Panes, Web-style UI’s are just as unobtrusive and straight forward as the Dialog-style counterparts.

Since I am working on a Task Pane infrastructure for an application at work, I was also interested in the details of the Web-style navigation library used to build Task Panes in .NET applications. The library allows programmers to create pages, which appear to be frame-like surfaces you can drop controls onto. The library manages a stack of those pages. This is different than our approach, which uses the MSHTML web browser component to host a stack of HTML pages.

Update: Part 2 is available.

Getting Started With P2P

I finally have a reason to try to implement P2P in a development project. I have used TCP sockets on several occasions and I have played around with UDP broadcasts as well. What I am trying to do with P2P needs to be more reliable than the UDP broadcasts.

The first thing I did was Google for “P2P framework library”. Unfortunately, not many relevant hits came back. The most popular was the Java-based JXTA toolkit. I work in C++, so it does not help me. Microsoft has a Peer-to-Peer toolkit, but it requires WinXP SP1 with Advanced Networking. I need to support OS’s older than that.

However, both toolkits appear to have exactly what I would in a P2P toolkit:

  • Discovery
  • Peer Groups
  • Messaging (including multicast)

Working in C++ on Win32 (Win9x/WinNT/Win2K/WinXP) keeps me from using these toolkits.

I would really like to use an existing toolkit to abstract the P2P implementation details. It’s going to be hard enough to get my own data sharing protocol working on top of P2P.

Other toolkits I have looked at include HOWL (an implementation of Zeroconf/Rendezvous) and BEEP. HOWL seems to only enable discovery. BEEP seems to only provide the messaging. I might look into merging the two together. I am also looking into wrappers for Multicast over Winsock.

Update: I have decided to work with Multicast for now. In the end, I’ll need to create some kind of message protocol at which point I’ll look at the RFC for BEEP.

Selling The Dream

It’s bound to happen sooner or later. Every so often Scoble (Microsoft blogging wunderkind) posts something that makes me question his sensibilities. One of his latest posts stopped me dead in my tracks. The post was a reply to a Jon Udell post on UI technologies in Longhorn. I thought Udell’s post asked questions any professional should be asking. Scoble tries to bring it into focus by saying:

Ahh, but Jon, the real play here is one of programmer productivity

Programmer productivity. As if every new technology/language introduced in the last few decades failed to deliver on that same promise. As if Longhorn and .NET are the productivity “tipping point.” As if being fluent in multiple technologies/languages is a bad thing. As if being fluent in multiple technologies/languages will go away.

Robert, your overselling the reality. I will use .NET to create commercial, shrinkwrap and enterprise software someday. I am becoming fluent in .NET technologies. But you should take a break from the Kool-Aid (although I hear the grape flavor is hard to put down). I see an advantage in understanding multiple technologies/languages and being able to choose the best for a particular situation. If anything, .NET will make the technology soup developers work with worse, not better. Just like every other “productivity improvement” before it.

High on XML

I have talked about our experimentation with XML as a file format. This is going very well. Since we started working with XML and its related technologies, other ideas started popping up. It’s not like XML is some magic concept, but it does open your mind enough to see other opportunities. Like creating a centralized Web Service to act as a repository for projects. You don’t need XML to do something like that, but since XML and Web Services go together, it’s easier to visualize how a service like that could be built off of (or into) our persistence system.

I think it’s because there are so many examples of XML being used for various things. Anytime you see XML being used for some purpose, you can ask yourself if your use of XML, whatever that may be, can be applied the way someone else uses XML. Another example could be syndication. RSS and Atom are XML-based syndication formats. Can I syndicate our file format? Maybe just modifications. I’ll have to float that one internally…

Task-based UI Design

Developing new products is great for trying new things. There is no legacy and lots of freedoms. One of our areas that is screaming for experimentation is the UI. UI models have been changing in recent years. Many products are taking more of a Web-look. If you start digging into the design reasons, you’ll run across discussions of Task-based UI or Inductive UI. It’s not hard to find examples, especially from Microsoft: Windows XP uses Task Panes and Web-isms in many parts of the Shell; Office XP also makes heavy use of Task Panes; Applications like Quicken and Money have very Web-like UI’s.

According to Microsoft, Task-based UI’s are suppose to address the following problems:

  • Users don’t seem to construct an adequate mental model of the product.
  • Even many long-time users never master common procedures.
  • Users must work hard to figure out each feature or screen.

The solution provided by Task-based UI’s is to provide simple, task oriented views that show the user what can be done now and what they can do next.

Obviously, slapping some Task Panes in your application doesn’t magically make it easier to use. This method is focused on the User Experience, but I would go further and say it’s User Assistance. As such, we are involving our Documentation team in establishing the design and guidelines for use in the application. In addition, this method also seems to play well with the teachings of Cooper and Raskin. We are looking forward to see the effects on overall usability.

RAD Test Scripting

The other day, some developer coworkers and I were talking about ways to make it easier for our Test group to create and maintain test scripts. Test scripts are a very important piece of the software development puzzle. But test scripts don’t find new defects. Test scripts are really good at finding breakage. That’s why scripts are run on new builds as BVT’s or release candidates as part of a larger regression suite. For me, few things are worse than uncontrolled breakage to keep a project from hitting deadlines.

To find new defects, you really can’t beat good test cases and exploratory testing. We wanted to find ways to keep our Test group working on finding new defects, not struggling to maintain scripts. Our current test scripting tool is just what it claims to be: A scripting tool. The problem with scripting is that it’s programming and programming brings a whole set of issues to the party. We’d like to hide those issues from most of the testers and only have a small team worry about them, if possible.

The genius idea we hit upon was a sort of RAD system for creating test scripts. Ah yes, well I did say we are developers. The general idea is to componentize the creation of scripts. Testers could piece together various script components to create test cases and suites. Of course, there would be a nice Windows application to allow testers to build their projects. We would save the project in an intermediate format and “compile” to native script that our scripting tool would execute.

We even found an additional benefit of the system: By maintaining our test suites in an intermediate format, we avoid script tool vendor lockin. When we switch to a new test scripting tool, we just need to write a new “compiler” to convert to the new script language. Vendor lockin is a real problem. How many commerical test scripting tools do you know use a common, portable language? It can make switching vendors very costly.

We are currently prototyping a system. I’ll let you know how it goes.