Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Advanced customization of WebBrowser Control in Dialog based app.

0.00/5 (No votes)
19 Aug 2003 3  
Shows how to customize the Microsoft WebBrowser control in a dialog based app to allow custom context menus, message boxes, windows, modal dialogs and C++ function calls from JavaScript to your application using window.external.

Sample image

Introduction

I am a web developer by day, but a C++/MFC coder by night! While I was doing my day job, our client requested that we provide them a solution to ensure that they had a way to allow their employees to take exams and view course content over the Internet in a more secure fashion than regular IE, so that their employees couldn't cheat or copy content and sell it on eBay very easily. So I figured if I could create my own window that goes full screen, I could hide the task bar, disable keystrokes, and whatever I needed to do to lock down the system while my app was open. However I needed to be able to customize the Internet Explorer Web Browser control pretty heavily (custom context menus, dialogs, windows, c++ calls from JavaScript, message boxes, and override keystrokes within the browser window).

I thought I would try coding it in C# and try some of this .NET stuff out on something more complex than a simple database application, but I quickly realized that video wouldn't even play in the HTML page I was loading through the C# implementation (Big problem for us considering we create interactive media solutions). Not to mention, having to make a function declaration for every raw Win32 API call I wanted to make, was really trying my patience. I even tried the .NET CF library, but it was missing many functions I needed. I ported some C++ code I had written to intercept keystrokes using a system wide hook, over to C# and when I did finally get it all ported over to C#, it crashed randomly and only trapped some of the keys I needed (C# may do garbage collection for you, but that doesn't mean your app won't crash). Needless to say, I just decided to start coding it in C++/MFC (long live C++ 6.0 MFC!).

Research or as I like to say (wandering aimlessly deep within the bowels of MSDN)

I began my journey of researching how to make a custom web browser using the Microsoft WebBrowser control that would allow me to have custom message boxes, custom modal dialogs, custom windows, custom context menus, call C++ code from JavaScript, and disable specific keystroke combinations. I found a ton of documents on MSDN and other sites telling me that I have to implement the IDocHostShowUI and IDocHostUIHandler COM interfaces and IDispatch something or rather. The only problem was I didn't know what that meant or how to do it. The source code I did find, only did a small part of what I needed to do and used a CHtmlView while I needed it to work in a dialog based application. The source code also left me in the dark on many of the other tasks I needed to do.

Using a single article that I found in the Microsoft knowledge base (after hours and hours of searching) and then writing and tweaking a little code, I was able to finally get all of the functionality I required in my custom web browser application.

How to create the custom web browser application

Before we start, I know this looks scary because of all the header files and CPP files we are going to have to add as well as the sheer length of this article, but it really isn't nearly as complicated or scary as it may seem. That's important to realize I think, because I know I almost got scared away when I started to read that knowledge base article too :)

I must say a lot of credit for this goes to Microsoft Knowledge Base Article - 236312. It was a big factor in allowing me to create a custom web browser solution that was flexible enough to meet my requirements.

First thing you have to do is create a dialog based application using the MFC AppWizard. Next go to the ResourceView tab in Visual Studio and open your dialog in design mode. Now go to "Projects", "Add to Project", "Components and Controls...". Scroll through the controls in the Registered ActiveX Controls folder until you find Microsoft Web Browser. Now click on that and click the insert button and then close the Components and Controls Gallery dialog window.

Now if you look at the controls toolbar in the design view of your dialog based app, you will see a new item that has a globe for an icon. That is our web browser control. So just click on that item and put it on your dialog box. Now press (Ctrl+W) to bring up the class wizard and add a member variable called m_browser for IDC_EXPLORER1.

Now here is where the knowledge base article I mentioned earlier comes into play. I would normally just refer you to the knowledge base article, but we all know how reliable Microsoft's links are. Therefore I will basically copy the instructions from the knowledge base article and paste below.

  1. Open CustomBrowser.h and add a public member variable as shown below to the CCustomBrowserApp class:
    public:
             class CImpIDispatch* m_pDispOM;
  2. Add a new header file to the project called Custsite.h. Copy and paste the following code into it:
    //=---------------------------------------------------------------=
    
    //  (C) Copyright 1996-1999 Microsoft Corporation. All Rights Reserved.
    
    //=---------------------------------------------------------------=
    
    #ifndef __CUSTOMSITEH__
    #define __CUSTOMSITEH__
    
    #include "idispimp.h"
    
    #include <MSHTMHST.H>
    
    
    // 
    
    // NOTE: 
    
    // Some of the code in this file is MFC implementation specific.
    
    // Changes in future versions of MFC implementation may require
    
    // the code to be changed. Please check the readme of this
    
    // sample for more information 
    
    // 
    
    
    class CCustomControlSite:public COleControlSite
    {
    public:
        CCustomControlSite(COleControlContainer *pCnt):
                                   COleControlSite(pCnt){}
    
    protected:
    
        DECLARE_INTERFACE_MAP();
    BEGIN_INTERFACE_PART(DocHostUIHandler, IDocHostUIHandler)
        STDMETHOD(ShowContextMenu)(/* [in] */ DWORD dwID,
                /* [in] */ POINT __RPC_FAR *ppt,
                /* [in] */ IUnknown __RPC_FAR *pcmdtReserved,
                /* [in] */ IDispatch __RPC_FAR *pdispReserved);
        STDMETHOD(GetHostInfo)( 
                /* [out][in] */ DOCHOSTUIINFO __RPC_FAR *pInfo);
        STDMETHOD(ShowUI)( 
            /* [in] */ DWORD dwID,
            /* [in] */ IOleInPlaceActiveObject __RPC_FAR *pActiveObject,
            /* [in] */ IOleCommandTarget __RPC_FAR *pCommandTarget,
            /* [in] */ IOleInPlaceFrame __RPC_FAR *pFrame,
            /* [in] */ IOleInPlaceUIWindow __RPC_FAR *pDoc);
        STDMETHOD(HideUI)(void);
        STDMETHOD(UpdateUI)(void);
        STDMETHOD(EnableModeless)(/* [in] */ BOOL fEnable);
        STDMETHOD(OnDocWindowActivate)(/* [in] */ BOOL fEnable);
        STDMETHOD(OnFrameWindowActivate)(/* [in] */ BOOL fEnable);
        STDMETHOD(ResizeBorder)( 
                /* [in] */ LPCRECT prcBorder,
                /* [in] */ IOleInPlaceUIWindow __RPC_FAR *pUIWindow,
                /* [in] */ BOOL fRameWindow);
        STDMETHOD(TranslateAccelerator)( 
                /* [in] */ LPMSG lpMsg,
                /* [in] */ const GUID __RPC_FAR *pguidCmdGroup,
                /* [in] */ DWORD nCmdID);
        STDMETHOD(GetOptionKeyPath)( 
                /* [out] */ LPOLESTR __RPC_FAR *pchKey,
                /* [in] */ DWORD dw);
        STDMETHOD(GetDropTarget)(
             /* [in] */ IDropTarget __RPC_FAR *pDropTarget,
             /* [out] */ IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget);
        STDMETHOD(GetExternal)( 
                /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppDispatch);
        STDMETHOD(TranslateUrl)( 
                /* [in] */ DWORD dwTranslate,
                /* [in] */ OLECHAR __RPC_FAR *pchURLIn,
                /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut);
        STDMETHOD(FilterDataObject)( 
                /* [in] */ IDataObject __RPC_FAR *pDO,
                /* [out] */ IDataObject __RPC_FAR *__RPC_FAR *ppDORet);
    END_INTERFACE_PART(DocHostUIHandler)
    };
    
    
    class CCustomOccManager :public COccManager
    {
    public:
        CCustomOccManager(){}
        COleControlSite* CreateSite(COleControlContainer* pCtrlCont)
        {
            CCustomControlSite *pSite = 
               new CCustomControlSite(pCtrlCont);
            return pSite;
        }
    };
    
    #endif
  3. Add a new CPP file called Custsite.cpp and add the code below to it:
    //=------------------------------------------------------------------=
    
    //  (C) Copyright 1996-1999 Microsoft Corporation. All Rights Reserved.
    
    //=------------------------------------------------------------------=
    
    
    
    // 
    
    // NOTE: 
    
    // Some of the code in this file is MFC implementation specific.
    
    // Changes in future versions of MFC implementation may require
    
    // the code to be changed. Please check the readme of this
    
    // sample for more information 
    
    // 
    
    
    #include "stdafx.h"
    
    #undef AFX_DATA
    #define AFX_DATA AFX_DATA_IMPORT
    #include "CustomBrowser.h"
    
    
    
    // NOTE: This line is a hardcoded reference to an MFC header file
    
    //  this path may need to be changed
    
    // to refer to the location of VC5 install
    
    //  for successful compilation.
    
    
    
    #include <..\src\occimpl.h>
    
    #undef AFX_DATA
    #define AFX_DATA AFX_DATA_EXPORT
    #include "custsite.h"
    
    
    
    
    BEGIN_INTERFACE_MAP(CCustomControlSite, COleControlSite)
        INTERFACE_PART(CCustomControlSite, 
        IID_IDocHostUIHandler, DocHostUIHandler)
    END_INTERFACE_MAP()
    
        
    
    ULONG FAR EXPORT  CCustomControlSite::XDocHostUIHandler::AddRef()
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        return pThis->ExternalAddRef();
    }
    
    
    ULONG FAR EXPORT  CCustomControlSite::XDocHostUIHandler::Release()
    {                            
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        return pThis->ExternalRelease();
    }
    
    HRESULT FAR EXPORT  
      CCustomControlSite::XDocHostUIHandler::QueryInterface(REFIID 
    
    riid, void **ppvObj)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        HRESULT hr = 
          (HRESULT)pThis->ExternalQueryInterface(&riid, ppvObj);
        return hr;
    }
    
    // * CImpIDocHostUIHandler::GetHostInfo
    
    // *
    
    // * Purpose: Called at initialization
    
    // *
    
    HRESULT FAR EXPORT  
      CCustomControlSite::XDocHostUIHandler::GetHostInfo( 
    
    DOCHOSTUIINFO* pInfo )
    {
    
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER;
        pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;
    
        return S_OK;
    }
    
    // * CImpIDocHostUIHandler::ShowUI
    
    // *
    
    // * Purpose: Called when MSHTML.DLL shows its UI
    
    // *
    
    HRESULT FAR EXPORT  
        CCustomControlSite::XDocHostUIHandler::ShowUI(
              DWORD dwID, 
              IOleInPlaceActiveObject * /*pActiveObject*/,
              IOleCommandTarget * pCommandTarget,
              IOleInPlaceFrame * /*pFrame*/,
              IOleInPlaceUIWindow * /*pDoc*/)
    {
    
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        // We've already got our own 
    
        // UI in place so just return S_OK
    
        return S_OK;
    }
    
    // * CImpIDocHostUIHandler::HideUI
    
    // *
    
    // * Purpose: Called when MSHTML.DLL hides its UI
    
    // *
    
    HRESULT FAR EXPORT  
      CCustomControlSite::XDocHostUIHandler::HideUI(void)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        return S_OK;
    }
    
    // * CImpIDocHostUIHandler::UpdateUI
    
    // *
    
    // * Purpose: Called when MSHTML.DLL updates its UI
    
    // *
    
    HRESULT FAR EXPORT  
      CCustomControlSite::XDocHostUIHandler::UpdateUI(void)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        // MFC is pretty good about updating
    
        // it's UI in it's Idle loop so I don't do 
    
    
    anything here
        return S_OK;
    }
    
    // * CImpIDocHostUIHandler::EnableModeless
    
    // *
    
    // * Purpose: Called from MSHTML.DLL's
    
    // * IOleInPlaceActiveObject::EnableModeless
    
    // *
    
    HRESULT FAR EXPORT  
      CCustomControlSite::XDocHostUIHandler::EnableModeless(BOOL 
    
    /*fEnable*/)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        return E_NOTIMPL;
    }
    
    // * CImpIDocHostUIHandler::OnDocWindowActivate
    
    // *
    
    // * Purpose: Called from MSHTML.DLL's 
    
    // * IOleInPlaceActiveObject::OnDocWindowActivate
    
    // *
    
    HRESULT FAR EXPORT 
      CCustomControlSite::XDocHostUIHandler::OnDocWindowActivate(BOOL 
    
    /*fActivate*/)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        return E_NOTIMPL;
    }
    
    // * CImpIDocHostUIHandler::OnFrameWindowActivate
    
    // *
    
    // * Purpose: Called from MSHTML.DLL's
    
    // * IOleInPlaceActiveObject::OnFrameWindowActivate
    
    // *
    
    HRESULT FAR EXPORT  
      CCustomControlSite::XDocHostUIHandler::OnFrameWindowActivate(BOOL 
    
    /*fActivate*/)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        return E_NOTIMPL;
    }
    
    // * CImpIDocHostUIHandler::ResizeBorder
    
    // *
    
    // * Purpose: Called from MSHTML.DLL's
    
    // * IOleInPlaceActiveObject::ResizeBorder
    
    // *
    
    HRESULT FAR EXPORT  
      CCustomControlSite::XDocHostUIHandler::ResizeBorder(
                    LPCRECT /*prcBorder*/, 
                    IOleInPlaceUIWindow* /*pUIWindow*/,
                    BOOL /*fRameWindow*/)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        return E_NOTIMPL;
    }
    
    // * CImpIDocHostUIHandler::ShowContextMenu
    
    // *
    
    // * Purpose: Called when MSHTML.DLL 
    
    // * would normally display its context menu
    
    // *
    
    HRESULT FAR EXPORT  
      CCustomControlSite::XDocHostUIHandler::ShowContextMenu(
                    DWORD /*dwID*/, 
                    POINT* /*pptPosition*/,
                    IUnknown* /*pCommandTarget*/,
                    IDispatch* /*pDispatchObjectHit*/)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        // We've shown our own context menu. 
    
        //MSHTML.DLL will no longer try
    
        return S_OK;  
    
    to show its own.
    }
    
    // * CImpIDocHostUIHandler::TranslateAccelerator
    
    // *
    
    // * Purpose: Called from MSHTML.DLL's TranslateAccelerator routines
    
    // *
    
    HRESULT FAR EXPORT 
      CCustomControlSite::XDocHostUIHandler::TranslateAccelerator(LPMSG 
    
    lpMsg,
                /* [in] */ const GUID __RPC_FAR *pguidCmdGroup,
                /* [in] */ DWORD nCmdID)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        return S_FALSE;
    }
    
    // * CImpIDocHostUIHandler::GetOptionKeyPath
    
    // *
    
    // * Purpose: Called by MSHTML.DLL
    
    // * to find where the host wishes to store 
    
    // *    its options in the registry
    
    // *
    
    HRESULT FAR EXPORT 
      CCustomControlSite::XDocHostUIHandler::GetOptionKeyPath(BSTR* 
    
    pbstrKey, DWORD)
    {
    
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        return E_NOTIMPL;
    }
    
    STDMETHODIMP CCustomControlSite::XDocHostUIHandler::GetDropTarget( 
            /* [in] */ IDropTarget __RPC_FAR *pDropTarget,
            /* [out] */ IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        return E_NOTIMPL;
    }
    
    
    STDMETHODIMP CCustomControlSite::XDocHostUIHandler::GetExternal( 
            /* [out] */ IDispatch __RPC_FAR *__RPC_FAR *ppDispatch)
    {
        // return the IDispatch we have for extending the object Model
    
        IDispatch* pDisp = (IDispatch*)theApp.m_pDispOM;
        pDisp->AddRef();
        *ppDispatch = pDisp;
        return S_OK;
    }
            
    STDMETHODIMP CCustomControlSite::XDocHostUIHandler::TranslateUrl( 
                /* [in] */ DWORD dwTranslate,
                /* [in] */ OLECHAR __RPC_FAR *pchURLIn,
                /* [out] */ OLECHAR __RPC_FAR *__RPC_FAR *ppchURLOut)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        return E_NOTIMPL;
    }
            
    STDMETHODIMP CCustomControlSite::XDocHostUIHandler::FilterDataObject( 
                /* [in] */ IDataObject __RPC_FAR *pDO,
                /* [out] */ IDataObject __RPC_FAR *__RPC_FAR *ppDORet)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        return E_NOTIMPL;
    }
  4. Add a new header file called Idispimp.h and add the following code to it:
    /*
     * IDispimp.H
     * IDispatch
     *
     * Copyright (c)1995-1999 Microsoft Corporation, All Rights Reserved
     */ 
    
    
    #ifndef _IDISPIMP_H_
    #define _IDISPIMP_H_
    
    class CImpIDispatch : public IDispatch
    {
        protected:
            ULONG               m_cRef;
    
        public:
            CImpIDispatch(void);
            ~CImpIDispatch(void);
    
            STDMETHODIMP QueryInterface(REFIID, void **);
            STDMETHODIMP_(ULONG) AddRef(void);
            STDMETHODIMP_(ULONG) Release(void);
    
            //IDispatch
    
            STDMETHODIMP GetTypeInfoCount(UINT* pctinfo);
            STDMETHODIMP GetTypeInfo(/* [in] */ UINT iTInfo,
                /* [in] */ LCID lcid,
                /* [out] */ ITypeInfo** ppTInfo);
            STDMETHODIMP GetIDsOfNames(
                /* [in] */ REFIID riid,
                /* [size_is][in] */ LPOLESTR *rgszNames,
                /* [in] */ UINT cNames,
                /* [in] */ LCID lcid,
                /* [size_is][out] */ DISPID *rgDispId);
            STDMETHODIMP Invoke(
                /* [in] */ DISPID dispIdMember,
                /* [in] */ REFIID riid,
                /* [in] */ LCID lcid,
                /* [in] */ WORD wFlags,
                /* [out][in] */ DISPPARAMS  *pDispParams,
                /* [out] */ VARIANT  *pVarResult,
                /* [out] */ EXCEPINFO *pExcepInfo,
                /* [out] */ UINT *puArgErr);
    
    };
    #endif //_IDISPIMP_H_
  5. Add a new CPP file called Idispimp.cpp and add the following code to it:
    /*
     * idispimp.CPP
     * IDispatch for Extending Dynamic HTML Object Model
     *
     * Copyright (c)1995-1999 Microsoft Corporation, All Rights Reserved
     */ 
    
    #include "stdafx.h"
    
    #include "idispimp.h"
    
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif
    
    
    // Hardcoded information for extending the Object Model 
    
    // Typically this would be supplied through a TypeInfo
    
    // In this case the name "xxyyzz" maps to DISPID_Extend 
    
    const    WCHAR pszExtend[10]=L"xxyyzz";
    #define DISPID_Extend 12345
    
    
    
    /*
     * CImpIDispatch::CImpIDispatch
     * CImpIDispatch::~CImpIDispatch
     *
     * Parameters (Constructor):
     *  pSite           PCSite of the site we're in.
     *  pUnkOuter       LPUNKNOWN to which we delegate.
     */ 
    
    CImpIDispatch::CImpIDispatch( void )
    {
        m_cRef = 0;
    }
    
    CImpIDispatch::~CImpIDispatch( void )
    {
        ASSERT( m_cRef == 0 );
    }
    
    
    /*
     * CImpIDispatch::QueryInterface
     * CImpIDispatch::AddRef
     * CImpIDispatch::Release
     *
     * Purpose:
     *  IUnknown members for CImpIDispatch object.
     */ 
    
    STDMETHODIMP CImpIDispatch::QueryInterface
                    ( REFIID riid, void **ppv )
    {
        *ppv = NULL;
    
    
        if ( IID_IDispatch == riid )
        {
            *ppv = this;
        }
        
        if ( NULL != *ppv )
        {
            ((LPUNKNOWN)*ppv)->AddRef();
            return NOERROR;
        }
    
        return E_NOINTERFACE;
    }
    
    
    STDMETHODIMP_(ULONG) CImpIDispatch::AddRef(void)
    {
        return ++m_cRef;
    }
    
    STDMETHODIMP_(ULONG) CImpIDispatch::Release(void)
    {
        return --m_cRef;
    }
    
    
    //IDispatch
    
    STDMETHODIMP CImpIDispatch::GetTypeInfoCount(UINT* /*pctinfo*/)
    {
        return E_NOTIMPL;
    }
    
    STDMETHODIMP CImpIDispatch::GetTypeInfo(/* [in] */ UINT /*iTInfo*/,
                /* [in] */ LCID /*lcid*/,
                /* [out] */ ITypeInfo** /*ppTInfo*/)
    {
        return E_NOTIMPL;
    }
    
    STDMETHODIMP CImpIDispatch::GetIDsOfNames(
                /* [in] */ REFIID riid,
                /* [size_is][in] */ OLECHAR** rgszNames,
                /* [in] */ UINT cNames,
                /* [in] */ LCID lcid,
                /* [size_is][out] */ DISPID* rgDispId)
    {
        HRESULT hr;
        UINT    i;
    
        // Assume some degree of success
    
        hr = NOERROR;
    
        // Hardcoded mapping for this sample
    
        // A more usual procedure would be to use a TypeInfo
    
        for ( i=0; i < cNames; i++)
        {
            if (  2 == CompareString( lcid, 
               NORM_IGNOREWIDTH, (char*)pszExtend, 
               3, (char*)rgszNames[i], 3 ) )
            {
                rgDispId[i] = DISPID_Extend;
            }
            else
            {
                // One or more are unknown so
    
                // set the return code accordingly
    
                hr = ResultFromScode(DISP_E_UNKNOWNNAME);
                rgDispId[i] = DISPID_UNKNOWN;
            }
        }
        return hr;
    }
    
    STDMETHODIMP CImpIDispatch::Invoke(
                /* [in] */ DISPID dispIdMember,
                /* [in] */ REFIID /*riid*/,
                /* [in] */ LCID /*lcid*/,
                /* [in] */ WORD wFlags,
                /* [out][in] */ DISPPARAMS* pDispParams,
                /* [out] */ VARIANT* pVarResult,
                /* [out] */ EXCEPINFO* /*pExcepInfo*/,
                /* [out] */ UINT* puArgErr)
    {
    
        // For this sample we only support
    
        // a Property Get on DISPID_Extend
    
        // returning a BSTR with "Wibble" as the value
    
        if ( dispIdMember == DISPID_Extend )
        {
            if ( wFlags & DISPATCH_PROPERTYGET )
            {
                if ( pVarResult != NULL )
                {
                    WCHAR buff[10]=L"Wibble";
                    BSTR bstrRet = SysAllocString( buff );
                    VariantInit(pVarResult);
                    V_VT(pVarResult)=VT_BSTR;
                    V_BSTR(pVarResult) = bstrRet;
                }
            }
        }
    
        return S_OK;
    }
  6. Open CustomBrowser.cpp and, in the InitInstance of CCustomBrowser, add the following code. Also comment out the call to AfxEnableControlContainer():
    BOOL 
    
    CCustomBrowserApp::InitInstance()
    {
        CCustomOccManager *pMgr = new CCustomOccManager;
    
        // Create an IDispatch class for
    
        // extending the Dynamic HTML Object Model 
    
        m_pDispOM = new CImpIDispatch;
    
        // Set our control containment up
    
        // but using our control container 
    
        // management class instead of MFC's default
    
        AfxEnableControlContainer(pMgr);
    
    
    //    AfxEnableControlContainer();
    
    //... rest of the code here
    
    }
  7. Also add the following to the list of include files in CustomBrowser.cpp:
    #include "afxpriv.h"
    
    #include <..\src\occimpl.h>
    
    #include "CustSite.h"
  8. Go to CustomBrowser.h and add the following statement at the bottom of the file.
    extern CCustomBrowserApp theApp;

Now that we have all of that done, we have a good start, but we still have ways to go. Basically what we have done so far is implement the IDocHostUIHandler and the IDispatch interfaces. This will allow us to do things like customize the context menus and call C++ functions from our JavaScript code using window.external. However, we also want to implement the IDocHostShowUI interface, so we can provide our own custom message boxes. We also need to write some extra code to make it possible to open modal and modeless dialog boxes using our custom web browser, create C++ functions that our JavaScript can call, and show a custom context menu.

We will start by implementing the IDocHostShowUI interface.

  1. Open the Custsite.h file and add the following code under public just below CCustomControlSite(COleControlContainer *pCnt):COleControlSite(pCnt){}:
    BEGIN_INTERFACE_PART(DocHostShowUI, 
    
    IDocHostShowUI)
            INIT_INTERFACE_PART(CDocHostSite, DocHostShowUI)
            STDMETHOD(ShowHelp)(
                /* [in ] */    HWND hwnd,
                /* [in ] */    LPOLESTR pszHelpFile,
                /* [in ] */    UINT uCommand,
                /* [in ] */    DWORD dwData,
                /* [in ] */    POINT ptMouse,
                /* [out] */    IDispatch __RPC_FAR *pDispatchObjectHit);
            STDMETHOD(ShowMessage)(
                /* [in ] */    HWND hwnd,
                /* [in ] */    LPOLESTR lpstrText,
                /* [in ] */    LPOLESTR lpstrCaption,
                /* [in ] */    DWORD dwType,
                /* [in ] */    LPOLESTR lpstrHelpFile,
                /* [in ] */    DWORD dwHelpContext,
                /* [out] */    LRESULT __RPC_FAR *plResult);
        END_INTERFACE_PART(DocHostShowUI)
  2. Open the Custsite.cpp file and add the following line of code that is shown in bold.
    BEGIN_INTERFACE_MAP(CCustomControlSite, COleControlSite)
        INTERFACE_PART(CCustomControlSite, 
               IID_IDocHostShowUI, DocHostShowUI)
        INTERFACE_PART(CCustomControlSite, 
               IID_IDocHostUIHandler, DocHostUIHandler)
    END_INTERFACE_MAP()
  3. Add the following code directly below the code shown above:
    ULONG 
    
    CCustomControlSite::XDocHostShowUI::AddRef()
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostShowUI);
    
        return pThis->ExternalAddRef();
    }
    
    ULONG CCustomControlSite::XDocHostShowUI::Release()
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostShowUI);
    
        return pThis->ExternalRelease();
    }
    
    HRESULT 
      CCustomControlSite::XDocHostShowUI::QueryInterface
      (REFIID riid, void ** ppvObj)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostShowUI);
    
        return pThis->ExternalQueryInterface( &riid, ppvObj );
    }
    
    
    HRESULT CCustomControlSite::XDocHostShowUI::ShowHelp(
            HWND hwnd, 
            LPOLESTR pszHelpFile, 
            UINT nCommand, 
            DWORD dwData, 
            POINT ptMouse, 
            IDispatch * pDispatchObjectHit)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostShowUI);
    
        return S_OK;
    }
    
    HRESULT CCustomControlSite::XDocHostShowUI::ShowMessage(
            HWND hwnd, 
            LPOLESTR lpstrText, 
            LPOLESTR lpstrCaption, 
            DWORD dwType, 
            LPOLESTR lpstrHelpFile, 
            DWORD dwHelpContext, 
            LRESULT * plResult)
    {
        METHOD_PROLOGUE(CCustomControlSite, DocHostShowUI);
        
        MessageBox(hwnd, (CString)lpstrText, "Custom Browser", dwType);
    
        return S_OK;
    }

Now we have finished implementing all the interfaces that we need in order to customize the browser to the extent we need to. Now all we have to do is write the code to actually do our customizations and we have a framework to use whenever we need to provide a custom web browser solution that requires advanced functionality.

First lets go into the Custsite.cpp file. In this file is where we can customize our message boxes. In other words every time someone calls the function alert('some text here'); from JavaScript they will see our custom message box. Find the function called ShowMessage and inside that function we will make it display a critical error image instead of the standard warning exclamation icon just for an example:

HRESULT CCustomControlSite::XDocHostShowUI::ShowMessage(HWND hwnd,
        LPOLESTR lpstrText,
        LPOLESTR lpstrCaption,
        DWORD dwType,
        LPOLESTR lpstrHelpFile,
        DWORD dwHelpContext,
        LRESULT * plResult)
{
    METHOD_PROLOGUE(CCustomControlSite, DocHostShowUI);
    
    //now all of our alerts will show the error icon instead of the

    //warning icon. Of course you could do a lot more here to customize

    //but this is just to show you for a simple example

    MessageBox(hwnd, (CString)lpstrText, "Custom Browser", 
/*dwType*/MB_ICONERROR);

    return S_OK;
}

If you want to, you can also trap common key combinations and cancel them or make them do different things. To disable some common key combinations go into the Custsite.cpp file and find the piece of code shown below:

HRESULT FAR EXPORT 
  CCustomControlSite::XDocHostUIHandler::TranslateAccelerator
  (LPMSG lpMsg,
            /* [in] */ const GUID __RPC_FAR *pguidCmdGroup,
            /* [in] */ DWORD nCmdID)
{
    METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
    return S_FALSE;
}

Replace the code shown above with the following code below:

    HRESULT FAR EXPORT  

CCustomControlSite::XDocHostUIHandler::TranslateAccelerator(LPMSG lpMsg,
            /* [in] */ const GUID __RPC_FAR *pguidCmdGroup,
            /* [in] */ DWORD nCmdID)
{
    METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
        
            //disable F5

        if(lpMsg->message == WM_KEYDOWN && 
        GetAsyncKeyState(VK_F5) < 0)
            return S_OK;

        if(GetKeyState(VK_CONTROL) & 0x8000)
        {
            //disable ctrl + O

            if(lpMsg->message == WM_KEYDOWN && 
            GetAsyncKeyState(0x4F) < 0)
                return S_OK;
            //disable ctrl + p

            if(lpMsg->message == WM_KEYDOWN && 
            GetAsyncKeyState(0x50) < 0)
                return S_OK;
            //disable ctrl + N

            if(lpMsg->message == WM_KEYDOWN && 
            GetAsyncKeyState(0x4E) < 0)
                return S_OK;
        }

        //disable back space

        if(lpMsg->wParam == VK_BACK)
            return S_OK;

    return S_FALSE;
}

You can also supply your own custom context menu instead of Internet Explorer's by putting code in the ShowContextMenu handler as shown below:

HRESULT FAR EXPORT  
   CCustomControlSite::XDocHostUIHandler::ShowContextMenu(
        DWORD /*dwID*/, 
        POINT* pptPosition,
        IUnknown* /*pCommandTarget*/,
        IDispatch* /*pDispatchObjectHit*/)
{
    METHOD_PROLOGUE(CCustomControlSite, DocHostUIHandler)
    
    //show our custom menu

    CMenu menu;
    menu.LoadMenu(IDR_CUSTOM_POPUP);
    CMenu* pSubMenu = menu.GetSubMenu(0);
    //Because we passed in theApp.m_pMainWnd all of our

    //WM_COMMAND handlers for the menu items must be handled

    //in CCustomBrowserApp. If you want this to be your dialog

    //you will have to grab a pointer to your dialog class and 

    //pass the hWnd of it into the last parameter in this call

    pSubMenu->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON, 
                pptPosition->x, pptPosition->y, 
                theApp.m_pMainWnd);

    // We've shown our own context menu. 

    //MSHTML.DLL will no longer try

    //to show its own.

    return S_OK;  
}

Now the cool part. Lets go into the file Idispimp.cpp. This is where we can write code to allows us to call our C++ functions from JavaScript on the HTML pages, that get loaded into our custom browser window.

First we must add the following code to the top of our Idispimp.cpp file just below the includes - just below #include "idispimp.h". We need this so we can access functions in our main dialog:

#include "CustomBrowser.h"

#include "CustomBrowserDlg.h"

Go to the top of the file until you see the code shown below:

// Hardcoded information for extending the Object Model 

// Typically this would be supplied through a TypeInfo

// In this case the name "xxyyzz" maps to DISPID_Extend 

const    WCHAR pszExtend[10]=L"xxyyzz";
#define DISPID_Extend 12345

Replace the block of code shown above with the code below:

//These are used to allow our application to determine

//what function it should call in our app based on the

//information passed to the IDispatch interface from

//javascript

CString cszCB_IsOurCustomBrowser = "CB_IsOurCustomBrowser";
CString cszCB_Close = "CB_Close";
CString cszCB_CustomFunction = "CB_CustomFunction";
CString cszCB_CustomFunctionWithParams = "CB_CustomFunctionWithParams";
CString cszCB_OpenWindow = "CB_OpenWindow";
CString cszCB_ShowModalDialog = "CB_ShowModalDialog";
CString cszCB_ShowModelessDialog = "CB_ShowModelessDialog";

#define DISPID_CB_IsOurCustomBrowser 1
#define DISPID_CB_Close 2
#define DISPID_CB_CustomFunction 3
#define DISPID_CB_CustomFunctionWithParams 4
#define DISPID_CB_OpenWindow 5
#define DISPID_CB_ShowModalDialog 6
#define DISPID_CB_ShowModelessDialog 7

Now we have to find the function GetIDsOfNames and write some code so it can pass the proper ID to the invoke function which will allow us to determine which C++ call to make. So find the code shown below:

STDMETHODIMP CImpIDispatch::GetIDsOfNames(
        /* [in] */ REFIID riid,
            /* [size_is][in] */ OLECHAR** rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID* rgDispId)
{
    HRESULT hr;
    UINT    i;

    // Assume some degree of success

    hr = NOERROR;

    // Hardcoded mapping for this sample

    // A more usual procedure would be to use a TypeInfo

    for ( i=0; i < cNames; i++)
    {
        if (  2 == CompareString( lcid, NORM_IGNOREWIDTH, (char*)pszExtend, 
3, (char*)rgszNames[i], 3 ) )
        {
            rgDispId[i] = DISPID_Extend;
        }
        else
        {
            // One or more are unknown so set the return code accordingly

            hr = ResultFromScode(DISP_E_UNKNOWNNAME);
            rgDispId[i] = DISPID_UNKNOWN;
        }
    }
    return hr;
}

Replace the code above with this code shown below:

STDMETHODIMP CImpIDispatch::GetIDsOfNames(
            /* [in] */ REFIID riid,
            /* [size_is][in] */ OLECHAR** rgszNames,
            /* [in] */ UINT cNames,
            /* [in] */ LCID lcid,
            /* [size_is][out] */ DISPID* rgDispId)
{
    HRESULT hr;
    UINT    i;

    // Assume some degree of success

    hr = NOERROR;


        for ( i=0; i < cNames; i++) {
        CString cszName  = rgszNames[i];

        if(cszName == cszCB_IsOurCustomBrowser)
        {
            rgDispId[i] = DISPID_CB_IsOurCustomBrowser;
        }
        else if(cszName == cszCB_Close)
        {
            rgDispId[i] = DISPID_CB_Close;
        }
        else if(cszName == cszCB_CustomFunction)
        {
            rgDispId[i] = DISPID_CB_CustomFunction;
        }
        else if(cszName == cszCB_CustomFunctionWithParams)
        {
            rgDispId[i] = DISPID_CB_CustomFunctionWithParams;
        }    
        else if(cszName == cszCB_OpenWindow)
        {
            rgDispId[i] = DISPID_CB_OpenWindow;
        }
        else if(cszName == cszCB_ShowModalDialog)
        {
            rgDispId[i] = DISPID_CB_ShowModalDialog;
        }
        else if(cszName == cszCB_ShowModelessDialog)
        {
            rgDispId[i] = DISPID_CB_ShowModelessDialog;
        }
        else {
            // One or more are unknown so set the return code accordingly

            hr = ResultFromScode(DISP_E_UNKNOWNNAME);
            rgDispId[i] = DISPID_UNKNOWN;
        }
    }
    return hr;
}

Now lastly find the Invoke function shown below:

STDMETHODIMP CImpIDispatch::Invoke(
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID /*riid*/,
            /* [in] */ LCID /*lcid*/,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS* pDispParams,
            /* [out] */ VARIANT* pVarResult,
            /* [out] */ EXCEPINFO* /*pExcepInfo*/,
            /* [out] */ UINT* puArgErr)
{

    // For this sample we only support a Property Get on DISPID_Extend

    // returning a BSTR with "Wibble" as the value

    if ( dispIdMember == DISPID_Extend )
    {
        if ( wFlags & DISPATCH_PROPERTYGET )
        {
            if ( pVarResult != NULL )
            {
                WCHAR buff[10]=L"Wibble";
                BSTR bstrRet = SysAllocString( buff );
                VariantInit(pVarResult);
                V_VT(pVarResult)=VT_BSTR;
                V_BSTR(pVarResult) = bstrRet;
            }
        }
    }

    return S_OK;
}

Replace the code above with this code shown below:

STDMETHODIMP CImpIDispatch::Invoke(
            /* [in] */ DISPID dispIdMember,
            /* [in] */ REFIID /*riid*/,
            /* [in] */ LCID /*lcid*/,
            /* [in] */ WORD wFlags,
            /* [out][in] */ DISPPARAMS* pDispParams,
            /* [out] */ VARIANT* pVarResult,
            /* [out] */ EXCEPINFO* /*pExcepInfo*/,
            /* [out] */ UINT* puArgErr)
{

    CCustomBrowserDlg* pDlg = (CCustomBrowserDlg*) AfxGetMainWnd();
    
    if(dispIdMember == DISPID_CB_IsOurCustomBrowser) 
    {
        if(wFlags & DISPATCH_PROPERTYGET)
        {
            if(pVarResult != NULL)
            {
                VariantInit(pVarResult);
                V_VT(pVarResult)=VT_BOOL;
                V_BOOL(pVarResult) = true;
            }
        }
        
        if ( wFlags & DISPATCH_METHOD )
        {
            bool bResult = pDlg->CB_IsOurCustomBrowser();
            
            VariantInit(pVarResult);
            V_VT(pVarResult)=VT_BOOL;
            V_BOOL(pVarResult) = bResult;
        }
        
    }
    
    if(dispIdMember == DISPID_CB_Close) 
    {
        if(wFlags & DISPATCH_PROPERTYGET)
        {
            if(pVarResult != NULL)
            {    
                VariantInit(pVarResult);
                V_VT(pVarResult)=VT_BOOL;
                V_BOOL(pVarResult) = true;
            }
        }
        
        if ( wFlags & DISPATCH_METHOD )
        {
            
            pDlg->CB_Close();
        }
        
    }
    
    
    if(dispIdMember == DISPID_CB_CustomFunction) 
    {
        if(wFlags & DISPATCH_PROPERTYGET)
        {
            if(pVarResult != NULL)
            {
                VariantInit(pVarResult);
                V_VT(pVarResult)=VT_BOOL;
                V_BOOL(pVarResult) = true;
            }
        }
        
        if ( wFlags & DISPATCH_METHOD )
        {
            pDlg->CB_CustomFunction();
        }
        
        
    }
    
    if(dispIdMember == DISPID_CB_CustomFunctionWithParams) 
    {
        if(wFlags & DISPATCH_PROPERTYGET)
        {
            if(pVarResult != NULL)
            {
                
                VariantInit(pVarResult);
                V_VT(pVarResult)=VT_BOOL;
                V_BOOL(pVarResult) = true;
            }
        }
        
        if ( wFlags & DISPATCH_METHOD )
        {
            //arguments come in reverse order

            //for some reason

            CString cszArg1= pDispParams->rgvarg[1].bstrVal;
            int nArg2= pDispParams->rgvarg[0].intVal;
            
            pDlg->CB_CustomFunctionWithParams(cszArg1, nArg2);
        }
        
    }

    if(dispIdMember == DISPID_CB_OpenWindow) 
    {
        if(wFlags & DISPATCH_PROPERTYGET)
        {
            if(pVarResult != NULL)
            {
                
                VariantInit(pVarResult);
                V_VT(pVarResult)=VT_BOOL;
                V_BOOL(pVarResult) = true;
            }
        }
        
        if ( wFlags & DISPATCH_METHOD )
        {
            //arguments come in reverse order

            //for some reason

            CString cszArg1= pDispParams->rgvarg[5].bstrVal;
            int nArg2= pDispParams->rgvarg[4].intVal;
            int nArg3= pDispParams->rgvarg[3].intVal;
            int nArg4= pDispParams->rgvarg[2].intVal;
            int nArg5= pDispParams->rgvarg[1].intVal;
            int nArg6 = pDispParams->rgvarg[0].intVal;
            
            pDlg->CB_OpenWindow(cszArg1, nArg2, 
            nArg3, nArg4, nArg5, nArg6);
        }
        
    }
    
    if(dispIdMember == DISPID_CB_ShowModelessDialog) 
    {
        if(wFlags & DISPATCH_PROPERTYGET)
        {
            if(pVarResult != NULL)
            {
                
                VariantInit(pVarResult);
                V_VT(pVarResult)=VT_BOOL;
                V_BOOL(pVarResult) = true;
            }
        }
        
        if ( wFlags & DISPATCH_METHOD )
        {
            //arguments come in reverse order

            //for some reason

            CString cszArg1= pDispParams->rgvarg[4].bstrVal;
            int nArg2= pDispParams->rgvarg[3].intVal;
            int nArg3= pDispParams->rgvarg[2].intVal;
            int nArg4= pDispParams->rgvarg[1].intVal;
            int nArg5= pDispParams->rgvarg[0].intVal;
            
            pDlg->CB_ShowModelessDialog(cszArg1, nArg2,
            nArg3, nArg4, nArg5);
        }
        
    }
    
    if(dispIdMember == DISPID_CB_ShowModalDialog) 
    {
        if(wFlags & DISPATCH_PROPERTYGET)
        {
            if(pVarResult != NULL)
            {
                
                VariantInit(pVarResult);
                V_VT(pVarResult)=VT_BOOL;
                V_BOOL(pVarResult) = true;
            }
        }
        
        if ( wFlags & DISPATCH_METHOD )
        {
            //arguments come in reverse order

            //for some reason

            CString cszArg1= pDispParams->rgvarg[4].bstrVal;
            int nArg2= pDispParams->rgvarg[3].intVal;
            int nArg3= pDispParams->rgvarg[2].intVal;
            int nArg4= pDispParams->rgvarg[1].intVal;
            int nArg5= pDispParams->rgvarg[0].intVal;
            
            pDlg->CB_ShowModalDialog(cszArg1, nArg2,
            nArg3, nArg4, nArg5);
        }
        
    }

    return S_OK;
}

Now we just need to add all these functions to our CCustomBrowserDlg class. So go into the CustomBrowserDlg.h file and add the following public function declarations as shown below:

    void CB_ShowModelessDialog(CString cszURL, 
    int nLeft, int nTop, int nWidth, int nHeight);

    void CB_ShowModalDialog(CString cszURL, 
    int nLeft, int nTop, int nWidth, int nHeight);

    void CB_OpenWindow(CString cszURL,
     int nLeft, int nTop, int nWidth, int nHeight, int nResizable);

    void CB_CustomFunctionWithParams(CString cszString, int nNumber);

    void CB_CustomFunction();

    void CB_Close();

    BOOL CB_IsOurCustomBrowser();

Now go into the CustomBrowserDlg.cpp file and add the following functions as shown below:

Note: The functions shown below just show a message box with all the parameters passed in just for simplicity sake. In the demo project, there is actually code to open a window, modal dialog, and modeless dialog. I just didn't want to explain how to create windows, modal, and modeless dialog boxes, as it is out of scope for this article. Of course you can see how all this code actually works in the demo project.

void CCustomBrowserDlg::CB_Close()
{
    AfxMessageBox("Close the browser here or the current window");

    //This is one way you can determine whether or not

    //to close a dialog or the main application depending

    //on if you call the CB_Close method from an html page

    //in a dialog/window or from an html page in the main app

    
    //for example if you launch a modal dialog from your javascript code

    //and you want to have your own close button in your html page as

    //an alternative to using the x button, then you can just call

    //window.external.CB_Close(); rather than window.close(); and this

    //function will determine which window to close based on which

    //window is currently active using the code below.

    
    /*
    CWnd* pWnd = GetActiveWindow();

    if(pWnd == this)
    {
        
        EndDialog(0);
    }
    else
    {
        CDialog* pWin = (CDialog*)pWnd;
        pWin->EndDialog(1);
    }
    */
}

void CCustomBrowserDlg::CB_CustomFunction()
{

    AfxMessageBox("Do whatever you like here!");
}

void CCustomBrowserDlg::CB_CustomFunctionWithParams
                     (CString cszString, int nNumber)
{
    CString cszParameters;
    cszParameters.Format
      ("parameter 1: %s\nparameter 2:  %d", cszString, nNumber);
    AfxMessageBox(cszParameters);
}

void CCustomBrowserDlg::CB_OpenWindow(CString cszURL, 
      int nLeft, int nTop, int 
      nWidth, int nHeight, int nResizable)
{
    //you could launch a normal window from here

    //I won't show how, but it is pretty easy.

    //I just wanted to show you how to pass parameters

    CString cszParameters;
    cszParameters.Format("URL=%s LEFT=%d TOP=%d 
    WIDTH=%d HEIGHT=%d RESIZABLE=%d", 
    cszURL, nLeft, nTop, nWidth, nHeight, nResizable);

    AfxMessageBox(cszParameters);
}

void CCustomBrowserDlg::CB_ShowModalDialog(CString cszURL, 
                int nLeft, int nTop, 
                int nWidth, int nHeight)
{
    //you could launch a modal dialog from here

    //I won't show how, but it is pretty easy.

    //I just wanted to show you how to pass parameters

    CString cszParameters;
    cszParameters.Format("URL=%s LEFT=%d TOP=%d
    WIDTH=%d HEIGHT=%d RESIZABLE=%d", 
    cszURL, nLeft, nTop, nWidth, nHeight);

    AfxMessageBox(cszParameters);
}

void CCustomBrowserDlg::CB_ShowModelessDialog
        (CString cszURL, int nLeft, int nTop, 
        int nWidth, int nHeight)
{
    //you could launch a modeless dialog from here

    //I won't show how, but it is pretty easy.

    //I just wanted to show you how to pass parameters

    CString cszParameters;
    cszParameters.Format("URL=%s LEFT=%d TOP=%d
    WIDTH=%d HEIGHT=%d RESIZABLE=%d", 
    cszURL, nLeft, nTop, nWidth, nHeight);

    AfxMessageBox(cszParameters);
}

All of these functions that we have added to our CCustomBrowserDlg class are now accessible from JavaScript! as shown below. The code shown below is from the CustomTest.html file that is included in the sample project.

    <script langauge="javascript">
    function fnIsOurCustomBrowser()
    {
        //Check to see if this is our custom

        //browser. We just see if the function 

        //IsOurCustomBrowser is available. By leaving

        //the brackets off of our function it is treated

        //as a property and thus allows us to check if

        //our method exists in the external window. If it

        //returns null then obviously this is not our custom

        //browser, so we return false.

        if(window.external.CB_IsOurCustomBrowser!=null)
            return true;
        else 
            return false;
    }    
    
    //setup a variable to let us know if this is our

    //custom browser.

    bIsCustomBrowser = fnIsOurCustomBrowser();

    if(!bIsCustomBrowser)
    {
        //You can check here to see if they have our custom browser.

        //If they don't you can do whatever you want, redirect them

        //to another page or whatever. For the purposes of this example

        //I will just alert them

        alert('You must be using Custom Browser to 
        view this page properly.');
    }

    function fnCallFunction()
    {
        //Call c++ function that we made

        //in our custom browser

        if(bIsCustomBrowser)
        window.external.CB_CustomFunction();
    }    

    function fnCallFunctionWithParams(strString, nNumber)
    {
        //Call c++ function that accepts parameters 

        //that we made in our custom browser

        if(bIsCustomBrowser)
        window.external.CB_CustomFunctionWithParams
                                (strString, nNumber);
    }

    function fnOpenWindow(strURL, nLeft, nTop, 
                   nWidth, nHeight, nResizable)
    {
        if(bIsCustomBrowser)
        window.external.CB_OpenWindow(strURL, nLeft, 
        nTop, nWidth, nHeight, nResizable);
    }

    function fnShowModalDialog(strURL, nLeft, 
                          nTop, nWidth, nHeight)
    {
        if(bIsCustomBrowser)
        window.external.CB_ShowModalDialog(strURL, nLeft, 
        nTop, nWidth, nHeight);
    }

    function fnShowModelessDialog(strURL, 
                 nLeft, nTop, nWidth, nHeight)
    {
        if(bIsCustomBrowser)
        window.external.CB_ShowModelessDialog(strURL, nLeft,
         nTop, nWidth, nHeight);
    }
    </script>

Points of interest

  • Creating a custom web browser that exhibits all of the functionality shown in this article is not as straight forward as one would think.
  • Once you have implemented the interfaces for your web browser control in your dialog based application, you have a pretty good framework to use for any custom browser solutions you need to provide.
  • I had to research for quite a few hours before I even found out what was necessary in order to provide the ability to call a C++ function from JavaScript.

Known problems

  • If you are having problems compiling the source code, then you will have to install the Internet Development SDK portion of the Platform SDK from Microsoft. Even if you already think you have installed the latest platform SDK, just install the Internet Development SDK again. This should fix the problem.

Credits

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here