dcsimg
 

IDocHostUIHandler Extended CHtmlView

Thursday Jul 29th 1999 by R. Lake

IDocHostUIHandler Extended CHtmlView

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
Home
Mobile Site | Full Site
Copyright 2018 © QuinStreet Inc. All Rights Reserved