This tutorial will help you build a mini browser using IE engine. It is based on WTL and uses a wrapper class which I've wrote for handling
IWebBrowser2 interface. Since I know it can be hard to read code, this tutorial will help you develop a mini browser - step by step.
Most of my projects are related to internet browsing. I'm using SDI with HTML view a lot of time. Sometimes, I need to use real browser capabilities, so I've written a wrapper for
IWebBrowser2. This wrapper class can handle a window which is embedded in IE. It can also handle events sinking in a very easy way (for example:
Creating a new project
We first start by creating a new WTL project. I assume you have WTL files installed (if not, check here). On the first wizard screen, select an SDI application and check for generating .CPP files.
On the second screen, change the default view to an HTML view.
First step will be to edit stdafx.h. Please include atlmisc.h (we'll use
CString from time to time) and atlctrlx.h (for
CMultiPaneStatusBarCtrl). We also need to comment out the
_ATL_DLL definition (we don't want our executable to depend on atl.dll) and to change IE version to version 5.
#define WINVER 0x0400
#define _WIN32_IE 0x0500
#define _RICHEDIT_VER 0x0100
extern CAppModule _Module;
Updating the view
In out view class, we need to include browser.h and inherit the view class from it. We also need to chain it to the message map so the class can handle several messages (
class CWTLBrowserView : public CWindowImpl<CWTLBrowserView,
CAxWindow>, public CWebBrowser2<CWTLBrowserView>
BOOL PreTranslateMessage(MSG* pMsg);
Creating the menu
We need to add some new items to the menu. A typical browser handles back, forward, home, stop and refresh. We'll add those commands to the menu and the toolbar.
Since some of the items aren't allowed from time to time, we need to handle their UI (we can't always use back and forward). First, we need to add them to the UI update map (in mainfrm.h).
We update them via the
CWebBrowser2 exposes 2 functions (
CanForward) which can determine the status of the back and forward actions.
Since the default wizard starts on microsoft.com, we need to change the code to about:blank, and start on the normal home page. We need to change the code in
m_hWndClient = m_view.Create(m_hWnd, rcDefault,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN |
WS_HSCROLL | WS_VSCROLL, WS_EX_CLIENTEDGE);
Creating the address bar
We now have a working framework, but we still can't enter any URL there! To solve the problem, we'll create an address bar, so the user can enter a URL for navigating. We'll add a new member variable (
CEdit) to our mainframe class - called
m_URL. We'll create and initialize it from
CMainFrame::OnCreate. Since we also want auto completion for our address bar, we'll use
SHAutoComplete function on the edit control.
AddSimpleReBarBand(hWndToolBar, NULL, TRUE);
If we try to compile the project, we'll get an error while linking the files. This error occurs since
SHAutoComplete is exported from shlwapi.dll. To solve it, we need to add the library (shlwapi.lib) to our project.
After compiling the project, we'll see our new bar waiting for us to type in. But hey! If we try to press the Enter key, our browser would just keep sleeping! Let's fix it!
Since the HTML framework forwards all keystrokes to the HTML document, we can't just wait for the
WM_CHAR message. We need to add some code to the
PreTranslateMessage function. We need to grab the
WM_CHAR message from the address bar and handle the
BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
if (pMsg->message==WM_CHAR && m_URL==pMsg->hwnd)
We can now enter a URL and watch our browser navigate it. But something is still missing. Why can't we use back, forward and the rest of our new commands? We need to add their functions to our mainframe class (since there is nothing special there, simply check the source code to see how it's done).
Let's test our browser. Try to go to http://www.codeproject.com/ and click on the WTL section. Why can't we see the new location in our address bar? To fix it, we need to update the address bar each time our location changes. A good place is to handle the
OnNavigateComplete2 from our view class. Since we need to update
m_URL which resides inside the mainframe class, we'll create a new reference to it inside our view class and pass the variable on the constructor. We now can handle our message and update the address bar.
void CWTLBrowserView::OnNavigateComplete2(IDispatch* pDisp, const String& szURL)
Another useful tweak includes a progress notification, secured icon, and status bar text. They all reside on the status bar. It's time to bring our big guns -
CMultiPaneStatusBarCtrl! We'll create a new variable inside the mainframe class, plus a reference inside our view class. The status bar should contain 3 parts: the default text, a secured icon, and a progress notification. Since the default text has a unique id (
ID_DEFAULT_PANE), all we need to do is to create additional two identifiers. From the "View->Resource Symbols" menu, we need to create our new symbols:
IDR_PROGRESS. After creating them, we can initialize our new status bar from the
We also need to add a new icon to our project (
IDI_LOCK) and load it into a variable (
m_hSecured). To handle proper UI updating, we'll add a new line to the UI update map:
In our view class, we'll add a new variable (
m_bSecured) and some code to handle our status bar updates:
void CWTLBrowserView::OnStatusTextChange(const String& szText)
void CWTLBrowserView::OnProgressChange(long nProgress, long nProgressMax)
void CWTLBrowserView::OnSetSecureLockIcon(long nSecureLockIcon)
Finally, we need to add some code to
m_StatusBar.SetPaneIcon(IDR_LOCK,m_view.IsSecured() ? m_hSecured : NULL);
To allow the browser to save and print files, we need to handle file messages. To send a command to the browser, we need to use the
ExecWB function. To query a command status (for the UI map), we can use the
QueryStatusWB function. We need to add the proper functions (for saving/printing), update the UI update map and handle their UI from the
OnIdle function. Since it is fairly simple, I won't include it in the article (check the source code for more details).
Edit commands are a special case. Since they can be used with the browser, as well as with our address bar, we need to check our focus window each time we use those commands. First, we add them to the UI update map, then we handle their update via the
UIEnable(ID_EDIT_CUT,m_view.QueryStatusWB(OLECMDID_CUT) & OLECMDF_ENABLED);
UIEnable(ID_EDIT_COPY,m_view.QueryStatusWB(OLECMDID_COPY) & OLECMDF_ENABLED);
UIEnable(ID_EDIT_PASTE,m_view.QueryStatusWB(OLECMDID_PASTE) & OLECMDF_ENABLED);
UIEnable(ID_EDIT_UNDO,m_view.QueryStatusWB(OLECMDID_UNDO) & OLECMDF_ENABLED);
We also need to distinguish them while executing the actual commands:
LRESULT CMainFrame::OnEditCut(WORD ,
WORD , HWND , BOOL& )
LRESULT CMainFrame::OnEditCopy(WORD ,
WORD , HWND , BOOL& )
LRESULT CMainFrame::OnEditPaste(WORD ,
WORD , HWND , BOOL& )
LRESULT CMainFrame::OnEditUndo(WORD ,
WORD , HWND , BOOL& )
Please excuse me for any typing/grammar mistakes I might have made. English is not my natural language, so I make mistakes from time to time. You can use any part of the code posted with the article. If you use it within your software, please email me and let me know (don't worry - no money will be asked for it). Special thanks to Michael Dunn, Ed Gadziemski and PJ Naughter. I've learned a lot from their articles.