Chinaunix首页 | 论坛 | 博客
  • 博客访问: 5833559
  • 博文数量: 671
  • 博客积分: 10010
  • 博客等级: 上将
  • 技术积分: 7310
  • 用 户 组: 普通用户
  • 注册时间: 2006-07-14 09:56
文章分类

全部博文(671)

文章存档

2011年(1)

2010年(2)

2009年(24)

2008年(271)

2007年(319)

2006年(54)

我的朋友

分类: C/C++

2008-08-27 13:38:04

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:
    Collapse
    //=---------------------------------------------------------------=
    //  (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:
    Collapse
    //=------------------------------------------------------------------=
    //  (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:
    Collapse
    /*
     * 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:
    Collapse
    /*
     * 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:
    Collapse
    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:

Collapse
    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:

Collapse
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:

Collapse
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:

Collapse
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:

Collapse
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.

Collapse
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.

Collapse
    
				
			  
阅读(1789) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~