The EX08B Example—The Web Browser ActiveX Control

Microsoft Internet Explorer 4.x has become a leading Web browser. I was surprised to find out that most of its functionality is contained in one big ActiveX control, Shdocvw.dll. When you run Internet Explorer, you launch a small shell program that loads this Web Browser control in its main window.

You can find complete documentation for the Web Browser control's properties, methods, and events in the Internet SDK, downloadable from http://www.microsoft.com. This documentation is in HTML form, of course.

Because of this modular architecture, you can write your own custom browser program with very little effort. EX08B creates a two-window browser that displays a search engine page side-by-side with the target page, as shown here.

Click to view at full size.

This view window contains two Web Browser controls that are sized to occupy the entire client area. When the user clicks an item in the search (right-hand) control, the program intercepts the command and routes it to the target (left-hand) control.

Here are the steps for building the example:

  1. Make sure the Web Browser control is registered. You undoubtedly have Microsoft Internet Explorer 4.x installed, since Visual C++ 6.0 requires it, so the Web Browser control should be registered. You can download Internet Explorer from http://www.microsoft.com if necessary.

  2. Run AppWizard to produce \vcpp32\ex08b\ex08b. Accept all the default settings but two: except select Single Document and deselect Printing And Print Preview. Make sure the ActiveX Controls option is checked as in EX08A.

  3. Install the Web Browser control in the EX08B project. Choose Add To Project from Visual C++'s Project menu, and choose Components And Controls from the submenu. Select Registered ActiveX Controls, and then choose Microsoft Web Browser. Visual C++ will generate the wrapper class CWebBrowser and add the files to your project.

  4. Add two CWebBrowser data members to the CEx08bView class. Click on the ClassView tab in the Workspace window, and then right-click the CEx08bView class. Choose Add Member Variable, and fill in the dialog as shown here.

    Repeat for m_target. ClassWizard adds an #include statement for the webbrowser.h file.

  5. Add the child window ID constants for the two controls. Select Resource Symbols from Visual C++'s View menu, and then add the symbols ID_BROWSER_SEARCH and ID_BROWSER_TARGET.

  6. Add a static character array data member for the AltaVista URL. Add the following static data member to the class declaration in ex08bView.h:

    private:
        static const char s_engineAltavista[];
    

    Then add the following definition in ex08bView.cpp, outside any function:

    const char CEx08bView::s_engineAltavista[] = "http://altavista.digital.com/";

  7. Use ClassWizard to map the view's WM_CREATE and WM_SIZE messages. Edit the handler code in ex08bView.cpp as follows:

    int CEx08bView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
    {
        if (CView::OnCreate(lpCreateStruct) == -1)
            return -1;
        
        DWORD dwStyle = WS_VISIBLE | WS_CHILD;    
        if (m_search.Create(NULL, dwStyle, CRect(0, 0, 100, 100),
                            this, ID_BROWSER_SEARCH) == 0) {
            AfxMessageBox("Unable to create search control!\n");
            return -1;
        }
        m_search.Navigate(s_engineAltavista, NULL, NULL, NULL, NULL);
    
        if (m_target.Create(NULL, dwStyle, CRect(0, 0, 100, 100),
                            this, ID_BROWSER_TARGET) == 0) {
            AfxMessageBox("Unable to create target control!\n");
            return -1;
        }
        m_target.GoHome(); // as defined in Internet Explorer 4 options
    
        return 0;
    }
    
    void CEx08bView::OnSize(UINT nType, int cx, int cy) 
    {
        CView::OnSize(nType, cx, cy);
    
        CRect rectClient;
        GetClientRect(rectClient);
        CRect rectBrowse(rectClient);
        rectBrowse.right = rectClient.right / 2;
        CRect rectSearch(rectClient);
        rectSearch.left = rectClient.right / 2;
    
        m_target.SetWidth(rectBrowse.right - rectBrowse.left);
        m_target.SetHeight(rectBrowse.bottom - rectBrowse.top);
        m_target.UpdateWindow();
    
        m_search.SetLeft(rectSearch.left);
        m_search.SetWidth(rectSearch.right - rectSearch.left);
        m_search.SetHeight(rectSearch.bottom - rectSearch.top);
        m_search.UpdateWindow();
    }

    The OnCreate function creates two browser windows inside the view window. The right-hand browser displays the top-level AltaVista page, and the left-hand browser displays the "home" page as defined through the Internet icon in the Control Panel. The OnSize function, called whenever the view window changes size, ensures that the browser windows completely cover the view window. The CWebBrowser member functions SetWidth and SetHeight set the browser's Width and Height properties.

  8. Add the event sink macros in the CEx08bView files. ClassWizard can't map events from a dynamic ActiveX control, so you must do it manually. Add the following lines inside the class declaration in the file ex08bView.h:

    protected:
        afx_msg void OnBeforeNavigateExplorer1(LPCTSTR URL, long Flags, LPCTSTR TargetFrameName, VARIANT FAR* PostData, LPCTSTR Headers, BOOL FAR* Cancel);
        afx_msg void OnTitleChangeExplorer2(LPCTSTR Text);
        DECLARE_EVENTSINK_MAP()

    Then add the following code in ex08bView.cpp:

    BEGIN_EVENTSINK_MAP(CEx08bView, CView)
        ON_EVENT(CEx08bView, ID_BROWSER_SEARCH, 100, OnBeforeNavigateExplorer1, VTS_BSTR VTS_I4 VTS_BSTR VTS_PVARIANT VTS_BSTR VTS_PBOOL)
        ON_EVENT(CEx08bView, ID_BROWSER_TARGET, 113,  OnTitleChangeExplorer2, VTS_BSTR)
    END_EVENTSINK_MAP()

  9. Add two event handler functions. Add the following member functions in ex08bView.cpp:

    void CEx08bView::OnBeforeNavigateExplorer1(LPCTSTR URL,
        long Flags, LPCTSTR TargetFrameName,
        VARIANT FAR* PostData, LPCTSTR Headers, BOOL FAR* Cancel)
    {
        TRACE("CEx08bView::OnBeforeNavigateExplorer1 -- URL = %s\n", URL);
    
        if (!strnicmp(URL, s_engineAltavista, strlen(s_engineAltavista))) {
            return;
        }
        m_target.Navigate(URL, NULL, NULL, PostData, NULL);
        *Cancel = TRUE;
    }
    
    void CEx08bView::OnTitleChangeExplorer2(LPCTSTR Text)
    {
        // Careful!  Event could fire before we're ready.
        CWnd* pWnd = AfxGetApp()->m_pMainWnd;
        if (pWnd != NULL) {
            if (::IsWindow(pWnd->m_hWnd)) {
                pWnd->SetWindowText(Text);
            }
        }
    }

    The OnBeforeNavigateExplorer1 handler is called when the user clicks on a link in the search page. The function compares the clicked URL (in the URL string parameter) with the search engine URL. If they match, the navigation proceeds in the search window; otherwise, the navigation is cancelled and the Navigate method is called for the target window. The OnTitleChangeExplorer2 handler updates the EX08B window title to match the title on the target page.

  10. Build and test the EX08B application. Search for something on the AltaVista page, and then watch the information appear in the target page.