IDocHostUIHandler Extended CHtmlView

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

I recently wanted to exploit the features of Internet Explorer to prevent users from selecting
text in my CHtmlView. The current implementation does not support a simple way to do this
(such as an EnableTextSelection function call), so I decided to investigate.

Here are some of the ‘hidden’ features I want to expose…

There is a Microsoft example called ‘Driller’ which is the basis of a solution, but the problem
is that it was written before the introduction of CHtmlView, so I began to enhance it.

What we need to do

The functionality we want is part of the IDocHostUIHandler interface. Therefore, we need to
find a way of replacing it with our own version. We do this by replacing the ActiveX control
container.

Step 1: Creating the container

We need to create two classes: one derived from COleControlSite, the other from COccManager.

Here is a rough outline of the format for ROleControlSite.h…


#include <mshtmhst.h>

class ROleControlSite : public COleControlSite
{
public:
ROleControlSite(COleControlContainer *pCnt = NULL):COleControlSite(pCnt) {}

protected:

DECLARE_INTERFACE_MAP();
BEGIN_INTERFACE_PART(DocHostUIHandler, IDocHostUIHandler)

// interface functions here

END_INTERFACE_PART(DocHostUIHandler)
BEGIN_INTERFACE_PART(DocHostShowUI, IDocHostShowUI)

// interface functions here

END_INTERFACE_PART(DocHostShowUI)
};

class ROccManager : public COccManager
{
public:
ROleControlSite *m_pROleControlSite;

ROccManager() { m_pROleControlSite = NULL; }

COleControlSite* CreateSite(COleControlContainer* pCtrlCont)
{
m_pROleControlSite = new ROleControlSite(pCtrlCont);
return m_pROleControlSite;
}
};

The functions for the interface are defined in ROleControlSite.cpp. This is where we tell
Internet Explorer the properties we want for the browser. It is very important that the
#include <../src/occimpl.h> is as written.

Here is a rough outline of the format for ROleControlSite.cpp…


#include “stdafx.h”
#undef AFX_DATA
#define AFX_DATA AFX_DATA_IMPORT
#include <..\src\occimpl.h>
#undef AFX_DATA
#define AFX_DATA AFX_DATA_EXPORT

#include “ROleControlSite.h”

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

BEGIN_INTERFACE_MAP(ROleControlSite, COleControlSite)
INTERFACE_PART(ROleControlSite, IID_IDocHostUIHandler, DocHostUIHandler)
INTERFACE_PART(ROleControlSite, IID_IDocHostShowUI, DocHostShowUI)
END_INTERFACE_MAP()

// implementation of the interface’s functions here
// …

Step 2: Using our container

When MFC creates a CHtmlView, it calls AfxEnableControlContainer(). This is the key to our
solution. If we pass our own container as a parameter, then it will use our interfaces that
we defined.

We add a new class derived from CHtmlView called RHtmlView. We then include the files
necessary to use our container in RHtmlView.cpp. All .cpp files which use RHtmlView must include
the following files, however the strict #include in ROleControlSite.cpp is not necessary here…


#include <..\src\occimpl.h>
#include “ROleControlSite.h”

Next, we add a member variable to RHtmlView for the container…


ROccManager m_pROccManager;

In our RHtmlView class, we replace the Create function with this…


BOOL RHtmlView::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle,
const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext)
{
///////////////////////////////////
// The following does the same as MFC source, except that
// AfxEnableControlContainer() is called with our handler.
///////////////////////////////////

CRect c_clientRect;
GetClientRect(&c_clientRect);

///////////////////////////////////
// create the view window:

m_pCreateContext = pContext;

if (!CView::Create(lpszClassName, lpszWindowName, dwStyle, rect,
pParentWnd, nID, pContext))
{
return FALSE;
}

AfxEnableControlContainer(&m_pROccManager);

///////////////////////////////////
// create the control window:

if (!m_wndBrowser.CreateControl(CLSID_WebBrowser, lpszWindowName,
WS_VISIBLE | WS_CHILD, c_clientRect, this, AFX_IDW_PANE_FIRST))
{
DestroyWindow();
return FALSE;
}

LPUNKNOWN lpUnk = m_wndBrowser.GetControlUnknown();
HRESULT hr = lpUnk->QueryInterface(IID_IWebBrowser2, (void**) &m_pBrowserApp);
if (!SUCCEEDED(hr))
{
m_pBrowserApp = NULL;
m_wndBrowser.DestroyWindow();
DestroyWindow();
return FALSE;
}

///////////////////////////////////
// Our initialisation:

SetWindowText(“RHtmlView”);
SetClassLong(this->m_hWnd, GCL_STYLE, CS_DBLCLKS);

///////////////////////////////////

return TRUE;
}

At this point, we have a working RHtmlView which uses our IDocHostUIHandler code. Note the
use of SetClassLong in RHtmlView::Create to remove irritating flicker when resizing.

Step 3: Specifying our own properties

When a page is loaded, the GetHostInfo function is called. This is where we specify flags
for the user interface of the control. Flags are defined in the MSDN Visual Studio Reference.

I have added extra functions to ROleControlSite to allow storage of the flags. The following
is our definition of GetHostInfo…


// GetHostInfo
STDMETHODIMP ROleControlSite::XDocHostUIHandler::GetHostInfo(
/* [i/o] */ DOCHOSTUIINFO __RPC_FAR *pInfo)
{
METHOD_PROLOGUE(ROleControlSite, DocHostUIHandler)

pInfo->cbSize = sizeof(DOCHOSTUIINFO);
pInfo->dwFlags = pThis->GetXDocHostUI_Flag();
pInfo->dwDoubleClick = pThis->GetXDocHostUI_DblClk();

return S_OK;
}

For the context-menu flag, we have to add some extra code like so…


// ShowContextMenu
STDMETHODIMP ROleControlSite::XDocHostUIHandler::ShowContextMenu(
/* [in ] */ DWORD dwID,
/* [in ] */ POINT __RPC_FAR *ppt,
/* [in ] */ IUnknown __RPC_FAR *pcmdtReserved,
/* [in ] */ IDispatch __RPC_FAR *pdispReserved)
{
METHOD_PROLOGUE(ROleControlSite, DocHostUIHandler)

// Don’t show context menu
if (pThis->GetXDocHostUI_Flag() & DOCHOSTUIFLAG_DISABLE_HELP_MENU) return S_OK;

// Otherwise, show default
return S_FALSE;
}

Step 4: Usability

With the implementation so far, we must take complicated steps to change the flags from
outside of ROleControlSite.cpp. Therefore, much of this is dealt with by adding functions to
RHtmlView. Here is the complete list…


// XDocHostUI_DblClk interface
DWORD GetXDocHostUI_DblClk();
void SetXDocHostUI_DblClk(DWORD dwSet);

// XDocHostUI_Flag interface
DWORD GetXDocHostUI_Flag();
void SetXDocHostUI_Flag(DWORD dwSet);

BOOL GetXDocHostUIFlag_Dialog();
BOOL GetXDocHostUIFlag_DisableHelpMenu();
BOOL GetXDocHostUIFlag_No3dBorder();
BOOL GetXDocHostUIFlag_ScrollNo();
BOOL GetXDocHostUIFlag_DisableScriptInactive();
BOOL GetXDocHostUIFlag_OpenNewWin();
BOOL GetXDocHostUIFlag_DisableOffscreen();
BOOL GetXDocHostUIFlag_FlatScrollbar();
BOOL GetXDocHostUIFlag_DivBlockDefault();
BOOL GetXDocHostUIFlag_ActivateClienthitOnly();

void SetXDocHostUIFlag_Dialog(BOOL bSet);
void SetXDocHostUIFlag_DisableHelpMenu(BOOL bSet);
void SetXDocHostUIFlag_No3dBorder(BOOL bSet);
void SetXDocHostUIFlag_ScrollNo(BOOL bSet);
void SetXDocHostUIFlag_DisableScriptInactive(BOOL bSet);
void SetXDocHostUIFlag_OpenNewWin(BOOL bSet);
void SetXDocHostUIFlag_DisableOffscreen(BOOL bSet);
void SetXDocHostUIFlag_FlatScrollbar(BOOL bSet);
void SetXDocHostUIFlag_DivBlockDefault(BOOL bSet);
void SetXDocHostUIFlag_ActivateClienthitOnly(BOOL bSet);

An important issue to note is that we must call Navigate2 with the current location in order
to dynamically change the properties, otherwise we see no effect. Therefore, I also updated
some standard CHtmlView functions…


void RHtmlView::SetRegisterAsBrowser(BOOL bNewValue)
{
CHtmlView::SetRegisterAsBrowser(bNewValue);
Navigate2(GetLocationURL(), 0, NULL, NULL);
}

void RHtmlView::SetRegisterAsDropTarget(BOOL bNewValue)
{
CHtmlView::SetRegisterAsDropTarget(bNewValue);
Navigate2(GetLocationURL(), 0, NULL, NULL);
}

void RHtmlView::SetSilent(BOOL bNewValue)
{
CHtmlView::SetSilent(bNewValue);
Navigate2(GetLocationURL(), 0, NULL, NULL);
}

void RHtmlView::SetTheaterMode(BOOL bNewValue)
{
CHtmlView::SetTheaterMode(bNewValue);
Navigate2(GetLocationURL(), 0, NULL, NULL);
}

Step 5: Adding RHtmlView to a project

First of all, create a view derived from CHtmlView. You should edit the source files, replacing
all references of CHtmlView with RHtmlView.

Add the files RHtmlView.cpp, RHtmlView.h, ROleControlSite.cpp, and ROleControlSite.h to your
project.

In all files which #include your view’s header file, #include the following before the view’s
header file is #included…


#include <..\src\occimpl.h>
#include “ROleControlSite.h”

You should now have the extra functionality. Note that all RHtmlView’s created can have different properties.

Possible Enhancements

  • Add functions to control downloading Images, Videos, and ActiveX controls. (the AtlBrowser
    sample does this)

  • Extend the DOM by implementing an IDispatch interface, and passing it in GetExternal.
  • Add the ability to stop the browser acting as a drag+drop server.

Download

Download demo project (and Release Executable) – 75 Kb

Download source – 6 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read