WebBrowser Customization
This tutorial shows you several ways to customize the "out of the box" behavior and appearance of the WebBrowser Control. You'll see how to use the advanced hosting interfaces IDocHostUIHandler, IDocHostUIHandler2, IDocHostShowUI, and ICustomDoc. This article will also look at other customization techniques such as download control through handling DISPID_AMBIENT_DLCONTROL in the host's IDispatch implementation, and using IHostDialogHelper.
This article is divided into the following sections:
- Prerequisites and Requirements
- Introduction
- WebBrowser Customization Architecture
- IDocHostUIHandler
- IDocHostUIHandler2
- Controlling Navigation
- IDocHostShowUI
- Controlling Download and Execution
- IHostDialogHelper
- Controlling New Windows
- Information Bar
- Conclusion
- Related Topics
Prerequisites and Requirements
In order to understand and use this tutorial, you need:
- A good understanding of C++ and the Component Object Model (COM).
- Familiarity with the Active Template Library (ATL).
- Microsoft Internet Explorer 6 or later installed.
- Header files and libraries for Internet Explorer 6 or later for use in your development environment; in particular, you need Mshtmhst.h.
Many of the WebBrowser customization features have been available since Internet Explorer 5 and Internet Explorer 5.5. Only a couple require Internet Explorer 6. One more requires Internet Explorer 6 for Microsoft Windows XP Service Pack 2 (SP2). Check the reference documentation for versioning information.
Introduction
Hosting the WebBrowser Control is a powerful tool for rapid application development. Through hosting, you can leverage easy-to-use technologiesDynamic HTML (DHTML), HTML, and XMLfor displaying content and developing a user interface (UI). However, "out of the box," the WebBrowser Control may not behave quite the way you want. For instance, in its default state the user can view the source of a displayed page by right-clicking and choosing the View Source option on the shortcut menu. You might want to disable or eliminate that option. You might also want to go even further and replace the default shortcut menu entirely with your own custom menu.
Aside from the customizations just mentioned, the advanced hosting features of Internet Explorer enable you to:
- Use buttons and other controls on a displayed page to call internal methods in your application, effectively extending the DHTML Object Model.
- Change drag-and-drop behavior.
- Control which pages your application can access, restricting navigation, for example, to specify pages, domains, or sites.
- Intercept user keystrokes and process them however you want. For example, you might intercept CTRL+O to prevent the user from opening a new page in Internet Explorer rather than the host application.
- Change default font and display settings.
- Control the kinds of content that are downloaded and what the WebBrowser Control does with them once they are downloaded. For example, you can prevent videos from playing, script from running, or new windows from opening when users click on links, or prevent Microsoft ActiveX controls from downloading or executing.
- Restrict View Source.
- Capture searches.
- Capture navigation errors.
- Replace or modify shortcut menus and shortcut menu entriesdisabling, replacing, customizing, or adding to them.
- Change registry settings for your application.
- Intercept and change messages dialogs shown by the WebBrowser Control.
- Control how new windows are created.
In the following sections, we'll look at many, though not all, of these possibilities and discuss how to implement them.
WebBrowser Customization Architecture
Introducing IDocHostUIHandler, IDocHostUIHander2, IDocHostShowUI, and ICustomDoc
Three interfaces are at the heart of WebBrowser ControlUI customization: IDocHostUIHandler, IDocHostUIHandler2, and IDocHostShowUI. These are interfaces that you implement in your application when you want to modify the WebBrowser Control. There are also a couple of service interfaces. ICustomDoc is implemented by MSHTML and provides a means to enable WebBrowser Control customization in some scenarios. IHostDialogHelper provides a means to open trusted dialog boxes, without identification that marks them as Internet Explorer dialog boxes.
In addition to using these interfaces, there are two other things you can do. For one, you can control download by intercepting ambient property changes in your IDispatch implementation. Second, you can control how new windows are created by intercepting DISPID_NEWWINDOW2 in your IDispatch implementation.
How It Works
The mechanism for WebBrowser Control customization is designed to be automated when a container provides support for ActiveX controls. Whenever the WebBrowser Control is instantiated, it attempts to find IDocHostUIHandler, IDocHostUIHandler2 and IDocHostShowUI implementations from the host, if they are available. The WebBrowser Control does this by a QueryInterface call on the host's IOleClientSite interface.
This architecture works automatically for an application that implements an IOleClientSite interface and that passes an IOleClientSite pointer to the WebBrowser Control through the browser's IOleObject::SetClientSite method. A typical instantiation of the WebBrowser Control might look like this:
SetClientSite(this;">// Error checking omitted for clarity
CComPtrspOleObj;
// Create WebBrowser--store pointer in class member variable m_spWebBrowser
CoCreateInstance(CLSID_WebBrowser, NULL, CLSCTX_INPROC, IID_IWebBrowser2, (void**)&m_spWebBrowser);
// Query WebBrowser for IOleObject pointer
m_spWebBrowser->QueryInterface(IID_IOleObject, (void**)&spOleObj);
// Set client site
spOleObj->SetClientSite(this);
// In-place activate the WebBrowser control
RECT rcClient
GetClientRect(&rcClient);
spOleObj->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, 0, GetTopLevelWindow(), &rcClient);
// Register container to intercept WebBrowser events
AtlAdvise(m_spWebBrowser, GetUnknown(), DIID_DWebBrowserEvents2, &m_dwCookie);
// Navigate to start page
m_spWebBrowser->Navigate(L"res://webhost.exe/startpage.htm", NULL, NULL, NULL, NULL);
However, if your application doesn't have an IOleClientSite interface, all is not lost. Internet Explorer provides the ICustomDoc interface so you can pass Internet Explorer your IDocHostUIHandler interface yourself. You cannot use IDocHostUIHandler2 and IDocHostShowUI without providing an IOleClientSite interface on the object hosting the WebBrowser Control.
When the WebBrowser Control holds a pointer to any of these interfaces, the methods of the interfaces are called at appropriate times during the lifetime of the WebBrowser Control. For instance, when the user right-clicks with the mouse anywhere in the WebBrowser Control's client area, your implementation of IDocHostUIHandler::ShowContextMenu will be called before Internet Explorer displays its default shortcut menu. This gives you a chance to display your own shortcut menu and prevent Internet Explorer from displaying its menu.
There are a few more important points to remember when initializing the WebBrowser Control. Your application should use OleInitialize rather than CoInitialize to start COM. OleInitialize enables support for the Clipboard, drag-and-drop operations, OLE, and in-place activation. Use OleUninitialize to close the COM library when your application shuts down.
The ATLCOM Wizard uses CoInitialize rather than OleInitialize to open the COM libraries. If you use this wizard to build an executable program, you need to change the CoInitialize and CoUninitialize calls to OleInitialize and OleUninitialize. For a Microsoft Foundation Classes (MFC) application, be sure that your application calls AfxOleInit, which calls OleInitialize, in its initialization process.
If you don't want drag-and-drop support in your application, you can call IWebBrowser2::RegisterAsDropTarget, passing in VARIANT_TRUE, to prevent any drag-and-drop operations on your WebBrowser Control instance.
An application hosting the WebBrowser Control will also need an implementation of IOleInPlaceSite, and since IOleInPlaceSite derives from IOleWindow, the application will also need an implementation of IOleWindow. You need these implementations so your application has a window in which to display the WebBrowser Control and so you can manage its display.
The implementations of these interfaces and IOleClientSite can be minimal or nonexistent in many cases. The IOleClientSite methods can all return E_NOTIMPL. Some of the IOleInPlaceSite and IOleWindow methods need an implementation beyond their return value. See the code sample for an example of a minimal implementation of IOleInPlaceSite and IOleWindow.
Now that we have covered the initialization preliminaries, let's take a look at each of the interfaces for WebBrowser Control customization.
IDocHostUIHandler
IDocHostUIHandler has been available since Internet Explorer 5. It provides fifteen methods. In general, some of the more important methods are IDocHostUIHandler::GetExternal, IDocHostUIHandler::GetHostInfo, IDocHostUIHandler::GetOptionKeyPath, IDocHostUIHandler::ShowContextMenu, and IDocHostUIHandler::TranslateAccelerator. Of course, the methods that are most important to you will depend on your application.
IDocHostUIHandler::GetHostInfo
You use IDocHostUIHandler::GetHostInfo to tell MSHTML about your application's capabilities and requirements. With it you can control a variety of things, for instance:
- You can disable the browser's 3-D border.
- You can prevent scroll bars or change their appearance.
- You can define how you want to handle double-clicks.
IDocHostUIHandler::GetHostInfo has one parameter, a pointer to a DOCHOSTUIINFO structure allocated by MSHTML. Your job is to fill this structure with the information you want to pass to MSHTML.
There are four members in the DOCHOSTUIINFO structure. The first member is cbSize, which is the size of the structure. You should set this yourself as the following code sample shows. The second member is dwFlags, which can take any number of flag values, combined with the bitwise OR operator, from the DOCHOSTUIFLAG enumeration. The third member is dwDoubleClick, which takes a value from the DOCHOSTUIDBLCLK enumeration. The fourth member is pchHostCss. You can set pchHostCss to a pointer to a string with Cascading Style Sheets (CSS) rules that you want to apply globally to any page displayed in the WebBrowser control. The final member of DOCHOSTUIINFO is pchHostNs. This is also a string pointer with which you can provide a semicolon-delimited list of namespaces. Use this member when you use custom tags on the pages you're displaying in the WebBrowser Control. This way you can have a global declaration for namespaces and don't have to declare them on each displayed page.
Be sure to use CoTaskMemAlloc to allocate strings for pchHostCss or pchHostNS.
HRESULT GetHostInfo(DOCHOSTUIINFO *pInfo)
{
WCHAR* szCSS = L"BODY {background-color:#ffcccc}";
WCHAR* szNS = L"IE;MyTags;MyTags2='www.microsoft.com'";
#define CCHMAX 256
size_t cchLengthCSS, cchLengthszNS;
HRESULT hr = StringCchLengthW(szCSS, CCHMAX, &cchLengthCSS);
// TODO: Add error handling code here.
OLECHAR* pCSSBuffer = (OLECHAR*)CoTaskMemAlloc((cchLengthCSS + 1) * sizeof(OLECHAR));
// TODO: Add error handling code to make sure memory block was allocated successfully.
hr = StringCchLengthW(szNS, CCHMAX, &cchLengthszNS);
// TODO: Add error handling code here.
OLECHAR* pNSBuffer = (OLECHAR*)CoTaskMemAlloc((cchLengthszNS + 1) * sizeof(OLECHAR));
// TODO: Add error handling code to make sure memory block was allocated successfully.
hr = StringCchCopyW(pCSSBuffer, cchLengthCSS + 1, szCSS);
// TODO: Add error handling code here.
hr = StringCchCopyW(pNSBuffer, cchLengthszNS + 1, szNS);
// TODO: Add error handling code here.
pInfo->cbSize = sizeof(DOCHOSTUIINFO);
pInfo->dwFlags = DOCHOSTUIFLAG_NO3DBORDER | DOCHOSTUIFLAG_SCROLL_NO;
pInfo->dwDoubleClick = DOCHOSTUIDBLCLK_DEFAULT;
pInfo->pchHostCss = pCSSBuffer;
pInfo->pchHostNS = pNSBuffer;
return S_OK;
}
If you have nothing special to say to MSHTML, you can return E_NOTIMPL from this method.
IDocHostUIHandler::ShowContextMenu
By implementing this method, you gain control over the shortcut menus displayed by the WebBrowser Control when a user right-clicks. You can prevent Internet Explorer from displaying its default menu by returning S_OK from this method. Returning some other value, like S_FALSE or E_NOTIMPL, allows Internet Explorer to go ahead with its default shortcut menu behavior.
If you return S_OK from this method and nothing more, you can prevent any right-click behavior by the WebBrowser Control. This may be all you desire in many scenarios but you can do more. Often, you use this method to create and display your own shortcut menu before returning S_OK. If you know the resources from which the WebBrowser Control displays its menus, and how it chooses them, you can also effectively customize the default WebBrowser Control shortcut menus themselves.
Refer to WebBrowser Customization (Part 2) for an implementation example of this method.
IDocHostUIHandler::GetExternal: Extending the DOM
IDocHostUIHandler provides you with a means to extend the Internet ExplorerDocument Object Model (DOM) with objects, methods, and properties of your own, implemented in your own application. You do this by providing MSHTML a pointer to the IDispatch interface for the COM automation object that implements your custom object, properties, and methods. These objects, properties, and methods will then be available to any page displayed in the WebBrowser Control through the document's external object.
You can easily implement this method, assuming your IDispatch interface is on the same object that implements IDocHostUIHandler.
HRESULT CBrowserHost::GetExternal(IDispatch **ppDispatch)
{
*ppDispatch = this;
return S_OK;
}
Once MSHTML has a pointer to your IDispatch, MSHTML will pass any calls on Web pages to automation methods on your application through the external object:
You can also use this technique to pass whole objects to a page. To do this, create a method in your IDispatch implementation that passes back the object you want to make available.
See the sample application for an example of IDispatch automation implementation using ATL.
IDocHostUIHandler::GetOptionKeyPath
IDocHostUIHandler::GetOptionKeyPath is a very powerful tool for customizing the WebBrowser Control. Many of the WebBrowser Control display and behavior settings are stored in the registry under the HKEY_CURRENT_USER key. IDocHostUIHandler::GetOptionKeyPath gives you an opportunity to override these registry settings for your specific instance of the WebBrowser Control. It does this by letting you supply an alternate registry location from which the WebBrowser Control will read in registry settings.
An implementation of IDocHostUIHandler::GetOptionKeyPath passes a string to the WebBrowser Control for the registry location you want it to read from. The WebBrowser Control will look for this key under the HKEY_CURRENT_USER key.
HRESULT CBrowserHost::GetOptionKeyPath(LPOLESTR *pchKey,
DWORD dwReserved)
{
HRESULT hr;
#define CCHMAX 256
size_t cchLength;
if (pchKey)
{
WCHAR* szMyKey = L"Software\\MyCompany\\MyApp";
hr = StringCchLengthW(szMyKey, CCHMAX, &cchLength);
// TODO: Add error handling code here.
*pchKey = (LPOLESTR)CoTaskMemAlloc((cchLength + 1) * sizeof(WCHAR));
if (*pchKey)
hr = StringCchCopyW(*pchKey, cchLength + 1, szKey);
// TODO: Add error handling code here.
hr = (*pchKey) ? S_OK : E_OUTOFMEMORY;
}
else
hr = E_INVALIDARG;
return hr;
}
As with IDocHostUIHandler::GetHostInfo, be sure to allocate memory for your strings using CoTaskMemAlloc.
Telling the WebBrowser Control where to look for your registry settings is the first stepactually, it's the second step as far as program execution is concerned. Your program must set the keys at the location specified by IDocHostUIHandler::GetOptionKeyPath so the WebBrowser Control can read them. There are a variety of ways to do this. One way would be with a registry script that runs when the application is installed. Another way might be to do it programmatically when the application starts. Here's a function that sets keys to change the default font, size, and color.
HRESULT SetSomeKeys()
{
HKEY hKey = NULL;
HKEY hKey2 = NULL;
HKEY hKey3 = NULL;
DWORD dwDisposition = NULL;
LONG lResult = NULL;
#define CBMAX 256
size_t cbLength;
RegCreateKeyEx(HKEY_CURRENT_USER, _T("Software\\MyCompany\\MyApp"),
NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE,
NULL, &hKey, &dwDisposition);
RegCreateKeyEx(hKey, _T("Main"), NULL, NULL, REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE, NULL, &hKey2, &dwDisposition);
RegSetValueEx(hKey2, _T("Use_DlgBox_Colors"), NULL, REG_SZ,
(CONST BYTE*)_T("no"), sizeof(_T("no")));
RegCloseKey(hKey2);
RegCreateKeyEx(hKey, _T("Settings"), NULL, NULL, REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE, NULL, &hKey2, &dwDisposition);
RegSetValueEx(hKey2, _T("Anchor Color"), NULL, REG_SZ,
(CONST BYTE*)_T("0,255,255"), sizeof(_T("0,255,255")));
RegSetValueEx(hKey2, _T("Text Color"), NULL, REG_SZ,
(CONST BYTE*)_T("255,0,255"), sizeof(_T("255,0,255")));
RegCloseKey(hKey2);
RegCreateKeyEx(hKey, _T("International\\Scripts"), NULL, NULL,
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL,
&hKey2, &dwDisposition);
BYTE bDefaultScript = 0x3;
RegSetValueEx(hKey2, _T("Default_Script"), NULL, REG_BINARY,
&bDefaultScript, sizeof(bDefaultScript));
RegCreateKeyEx(hKey2, _T("3"), NULL, NULL, REG_OPTION_NON_VOLATILE,
KEY_SET_VALUE, NULL, &hKey3, &dwDisposition);
BYTE bSize = 0x4; // Value from 0 - 4. 2 is medium.
TCHAR* szFontName = _T("Comic Sans MS");
TCHAR* szFixedFontName = _T("Courier");
HRESULT hr = StringCbLength(szFontName, CBMAX, &cbLength);
// TODO: Add error handling code here.
RegSetValueEx(hKey3, _T("IEPropFontName"), NULL, REG_SZ,
(CONST BYTE*)szFontName, cbLength + sizeof(TCHAR));
hr = StringCbLength(szFixedFontName, CBMAX, &cbLength);
// TODO: Add error handling code here.
RegSetValueEx(hKey3, _T("IEFixedFontName"), NULL, REG_SZ,
(CONST BYTE*)szFixedFontName, cbLength + sizeof(TCHAR));
RegSetValueEx(hKey3, _T("IEFontSize"), NULL, REG_BINARY, &bSize, sizeof(bSize));
RegCloseKey(hKey3);
RegCloseKey(hKey2);
RegCloseKey(hKey);
return S_OK;
}
IDocHostUIHandler2
IDocHostUIHandler2 has a single method, IDocHostUIHandler2::GetOverrideKeyPath. It performs a function very similar to IDocHostUIHandler::GetOptionKeyPath. It points your hosted WebBrowser to registry settings to modify the default Internet Explorer registry settings. An implementation of IDocHostUIHandler2::GetOverrideKeyPath will look much the same as an implementation of IDocHostUIHandler::GetOptionKeyPath.
GetOptionKeyPath and GetOverrideKeyPath Compared
The difference between IDocHostUIHandler::GetOptionKeyPath and IDocHostUIHandler2::GetOverrideKeyPath is subtle, but significant. If you implement IDocHostUIHandler::GetOptionKeyPath, your WebBrowser Control instance will ignore any user settings for Internet Explorer. These settings are stored in the registry under HKEY_CURRENT_USER/Software/Microsoft/Internet Explorer. If you implement IDocHostUIHandler2::GetOverrideKeyPath, your WebBrowser Control instance will incorporate any user settingsfont settings, menu extensions, and so forthinto the way it displays and behaves.
To illustrate the difference between IDocHostUIHandler::GetOptionKeyPath and IDocHostUIHandler2::GetOverrideKeyPath, refer to the code example for adding extensions to the context menu in WebBrowser Customization (Part 2). Recall the line:
spCT->Exec(&CGID_ShellDocView, SHDVID_ADDMENUEXTENSIONS, 0, &var1, &var2);
If you've implemented IDocHostUIHandler::GetOptionKeyPath, this line would add none of the menu extension information that is stored in the registry for the current user. Instead, it would only add menu items that you have created. If you've implemented IDocHostUIHandler2::GetOverrideKeyPath, this line would add any extensions defined for the current user under
Controlling Navigation
You may have wondered why the section on IDocHostUIHandler didn't mention IDocHostUIHandler::TranslateUrl as a method to implement when you wish to control page navigation. The reason is that this method is not the most general purpose technique with which to control navigation. Unless you are hosting MSHTML directly, this method will have no effect on navigation. Instead, you can control navigation by implementing your IDispatch::Invoke method to handle DISPID_BEFORENAVIGATE2. As an example, the following code prevents navigation to a particular URL, displaying a "No Navigation Allowed" error page if the user attempts to do so.
case DISPID_BEFORENAVIGATE2:
{
CComBSTR url = ((*pDispParams).rgvarg)[5].pvarVal->bstrVal;
if (url == "http://www.adatum.com" || url == "http://www.adatum.com/")
{
CComPtrspBrowser;
CComPtrspDisp = ((*pDispParams).rgvarg)[6].pdispVal;
spDisp->QueryInterface(IID_IWebBrowser2, (void**)&spBrowser);
spBrowser->Stop();
CComBSTR newURL = "L"res://webhost.exe/nonavigate.htm";
spBrowser->Navigate(newURL, NULL, NULL, NULL, NULL);
((*pDispParams).rgvarg)[0].boolVal = TRUE;
}
break;
}
IDocHostShowUI
This interface gives you control over the message boxes and help files displayed by the WebBrowser Control. It works the same way as IDocHostUIHandler and IDocHostUIHandler2 in that you implement it so the WebBrowser Control can call your IDocHostShowUI methods before it displays any messages or help menus of its own. This gives you a chance to prevent the WebBrowser Control from displaying anything and enables you to display your own custom message or help instead. IDocHostShowUI has two methods, IDocHostShowUI::ShowMessage and IDocHostShowUI::ShowHelp.
IDocHostShowUI::ShowMessage
Return S_OK to disable WebBrowser Control messages. Any other return value, like S_FALSE or E_NOTIMPL, allows the WebBrowser Control to display with its message box.
One nice thing you can do with this method is customize the message box captions for your application so they don't read "Microsoft Internet Explorer." You can do this by comparing the caption string in lpstrCaption with the string resource Internet Explorer uses, which is stored in Shdoclc.dll. It is identified by the symbol IDS_MESSAGE_BOX_TITLE, whose value is 2213. The following code snippet shows how you might do this.
HRESULT CBrowserHost::ShowMessage(HWND hwnd,
LPOLESTR lpstrText,
LPOLESTR lpstrCaption,
DWORD dwType,
LPOLESTR lpstrHelpFile,
DWORD dwHelpContext,
LRESULT *plResult)
{
USES_CONVERSION;
TCHAR pBuffer[50];
// resource identifier for window caption "Microsoft Internet Explorer"
#define IDS_MESSAGE_BOX_TITLE 2213
// Load Shdoclc.dll and the IE message box title string
HINSTANCE hinstSHDOCLC = LoadLibrary(TEXT("SHDOCLC.DLL"));
if (hinstSHDOCLC == NULL)
{
// Error loading module -- fail as securely as possible
return;
}
LoadString(hinstSHDOCLC, IDS_MESSAGE_BOX_TITLE, pBuffer, 50);
// Compare the IE message box title string with lpstrCaption
// If they're the same, substitute your own Caption
if (_tcscmp(OLE2T(lpstrCaption), pBuffer) == 0)
lpstrCaption = L"Custom Caption";
// Create your own message box and display it
*plResult = MessageBox(OLE2T(lpstrText), OLE2T(lpstrCaption), dwType);
// Unload Shdoclc.dll and return
FreeLibrary(hinstSHDOCLC);
return S_OK;
}
IDocHostShowUI::ShowHelp
This method is called whenever Internet Explorer Help would be shown, for instance when the F1 key is pressed, and works analogously to IDocHostShowUI::ShowMessage. Return S_OK to override Internet Explorer Help, or another HRESULT value to let Internet Explorer proceed with its Help.
Controlling Download and Execution
The WebBrowser Control gives you control over what it downloads, displays, and executes. To gain this control, you need to implement your host's IDispatch so it handles DISPID_AMBIENT_DLCONTROL. When the WebBrowser Control is instantiated, it will call your IDispatch::Invoke with this ID. Set pvarResult to a combination of following flags, using the bitwise OR operator, to indicate your preferences.
- DLCTL_DLIMAGES, DLCTL_VIDEOS, and DLCTL_BGSOUNDS: Images, videos, and background sounds will be downloaded from the server and displayed or played if these flags are set. They will not be downloaded and displayed if the flags are not set.
- DLCTL_NO_SCRIPTS and DLCTL_NO_JAVA: Scripts and Java applets will not be executed.
- DLCTL_NO_DLACTIVEXCTLS and DLCTL_NO_RUNACTIVEXCTLS : ActiveX controls will not be downloaded or will not be executed.
- DLCTL_DOWNLOADONLY: The page will only be downloaded, not displayed.
- DLCTL_NO_FRAMEDOWNLOAD: The WebBrowser Control will download and parse a frameSet, but not the individual frame objects within the frameSet.
- DLCTL_RESYNCHRONIZE and DLCTL_PRAGMA_NO_CACHE: These flags cause cache refreshes. With DLCTL_RESYNCHRONIZE, the server will be asked for update status. Cached files will be used if the server indicates that the cached information is up-to-date. With DLCTL_PRAGMA_NO_CACHE, files will be re-downloaded from the server regardless of the update status of the files.
- DLCTL_NO_BEHAVIORS: Behaviors are not downloaded and are disabled in the document.
- DLCTL_NO_METACHARSET_HTML: Character sets specified in meta elements are suppressed.
- DLCTL_URL_ENCODING_DISABLE_UTF8 and DLCTL_URL_ENCODING_ENABLE_UTF8: These flags function similarly to the DOCHOSTUIFLAG_URL_ENCODING_DISABLE_UTF8 and DOCHOSTUIFLAG_URL_ENCODING_ENABLE_UTF8 flags used with IDocHostUIHandler::GetHostInfo. The difference is that the DOCHOSTUIFLAG flags are checked only when the WebBrowser Control is first instantiated. The download flags here for the ambient property change are checked whenever the WebBrowser Control needs to perform a download.
- DLCTL_NO_CLIENTPULL: No client pull operations will be performed.
- DLCTL_SILENT: No user interface will be displayed during downloads.
- DLCTL_FORCEOFFLINE: The WebBrowser Control always operates in offline mode.
- DLCTL_OFFLINEIFNOTCONNECTED and DLCTL_OFFLINE: These flags are the same. The WebBrowser Control will operate in offline mode if not connected to the Internet.
DISPID_AMBIENT_DLCONTROL and the flag values are defined in mshtmdid.h.
Initially, when the function call to IDispatch::Invoke starts, the VARIANT to which the parameter pvarResult points is of type VT_EMPTY. You must switch the type to VT_I4 for any settings to have an effect. You can place your flag values in the lVal member of the VARIANT.
Most of the flag values have negative effects, that is, they prevent behavior that normally happens. For instance, scripts are normally executed by the WebBrowser Control if you don't customize its behavior. But if you set the DLCTL_NOSCRIPTS flag, no scripts will execute in that instance of the control. However, three flagsDLCTL_DLIMAGES, DLCTL_VIDEOS, and DLCTL_BGSOUNDSwork exactly opposite. If you set flags at all, you must set these three for the WebBrowser Control to behave in its default manner vis-a-vis images, videos and sounds.
The following code sample causes a WebBrowser Control instance to download and display images and videos, but not background sounds, since the DLCTL_BGSOUNDS is not explicitly set. Also, script execution on pages displayed by the WebBrowser Control is disabled.
STDMETHODIMP CAtlBrCon::Invoke(DISPID dispidMember, REFIID riid,
LCID lcid, WORD wFlags,
DISPPARAMS* pDispParams,
VARIANT* pvarResult,
EXCEPINFO* pExcepInfo,
UINT* puArgErr)
{
switch (dispidMember)
{
case DISPID_AMBIENT_DLCONTROL:
pvarResult->vt = VT_I4;
pvarResult->lVal = DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_NO_SCRIPTS;
break;
default:
return DISP_E_MEMBERNOTFOUND;
}
return S_OK;
}
IHostDialogHelper
IHostDialogHelper is an interface you can use to create dialog boxes according to your liking. This interface has one method, IHostDialogHelper::ShowHTMLDialog. This method provides the same service as the function ShowHTMLDialog, but it's a little easier to use.
To use IHostDialogHelper, you create the dialog helper object from scratch. Here's how you would do it using CoCreateInstance. The interface and IDs are defined in mshtmhst.h.
IHostDialogHelper* pHDH;
IMoniker* pUrlMoniker;
BSTR bstrOptions = SysAllocString(L"dialogHeight:30;dialogWidth:40");
BSTR bstrPath = SysAllocString(L"c:\\dialog.htm");
CreateURLMoniker(NULL, bstrPath, &pUrlMoniker);
// Create the dialog helper object
CoCreateInstance(CLSID_HostDialogHelper,
NULL,
CLSCTX_INPROC,
IID_IHostDialogHelper,
(void**)&pHDH);
// Call ShowHTMLDialog to create your dialog box
pHDH->ShowHTMLDialog(NULL,
pUrlMoniker,
NULL,
bstrOptions,
NULL,
NULL);
// Free resources
SysFreeString(bstrPath);
SysFreeString(bstrOptions);
pUrlMoniker->Release();
pHDH->Release();
Controlling New Windows
One important way to take control of the WebBrowser Control is to control navigation. You saw earlier how you can intercept DISPID_BEFORENAVIGATE2 in an IDispatch::Invoke implementation to control where your WebBrowser Control will navigate. Another important aspect of navigation is to control how the navigation occurs, especially when opening new windows. Let's say, for instance, that the user clicks the right mouse button over a link and chooses "Open in New Window" or that a page contains a script like this:
window.open("www.msn.com");
By default, the WebBrowser Control deals with this code by opening a new instance of Internet Explorer to display the page. This may be fine for your application. But then again, it may not. Perhaps you'll want all links to open in your current WebBrowser Control instance. Or perhaps you'll want to open a link in a new WebBrowser Control instance under your control, with your user interface and with your branding.
You can intercept an event, DWebBrowserEvents2::NewWindow2, in your IDispatch implementation to control this. Your control needs to connect to the DWebBrowserEvents2 connection point to intercept this event.
Once you're connected to DWebBrowserEvents2, implement your IDispatch::Invoke so that it handles DISPID_NEWWINDOW2. During the IDispatch::Invoke function call for DISPID_NEWWINDOW2, the array pDispParams contains two parameters. The first one, at index zero, is a Boolean value that tells the WebBrowser Control whether to cancel the new window or not. By default, it is FALSE and a new window will open. If you want to cancel new window creation completely, set the flag to TRUE.
The parameter at index one is a pointer to an IDispatch interface. You can set this parameter to the IDispatch of a WebBrowser Control that you've created. When you pass back an IDispatch like this, MSHTML will use the control you've given it to open the link.
Information Bar
Internet Explorer 6 for Windows XP SP2 introduced a new security UI element called the Information Bar. The Information Bar displays a UI element to users of Internet Explorer when certain actions are prevented. Specifically, it displays when the following are blocked.
- Pop-up window instantiation (see Pop-up Blocker)
- File downloads (see File Download Restrictions)
- ActiveX control installation (see ActiveX Restrictions)
- ActiveX control security prompts because of the user's security settings or because the control is not marked safe for scripting
- Files that have a mismatch between the file extension and the MIME type (see MIME Handling)
- Content restricted by the network protocol lockdown (see Protocols)
The Information Bar is one of the feature controls introduced in Internet Explorer 6 for Windows XP SP2. Like the other feature controls, it is managed through a registry key (FEATURE_SECURITYBAND). Internet Explorer (iexplorer.exe) and Windows Explorer (explorer.exe) run under this feature control by default. The following shows the registry key and enabled processes.
- HKEY_LOCAL_MACHINE (or HKEY_CURRENT_USER)
- SOFTWARE
- Microsoft
- Internet Explorer
- Main
- FeatureControl
- FEATURE_SECURITYBAND
- iexplorer.exe= 0x00000001
- explorer.exe= 0x00000001
- process name.exe=0x00000001
The FEATURE_SECURITYBAND feature control only affects whether Internet Explorer displays the Information Bar, which alerts the user that an action has been mitigated. It does not control the mitigation of the action.
An application hosting the WebBrowser Control can enable the Information Bar by adding its process to this registry key. This can be done programmatically by using the CoInternetSetFeatureEnabled function. If an application does not run under this feature control, the WebBrowser Control behaves the same as Internet Explorer 6 SP1b.
There is no access to this feature through script.
Applications running under the FEATURE_SECURITYBAND and related feature controls can also leverage the Information BarAPIs to customize the UI displayed when a URL action is disallowed. There are several new OLECMDID commands for the Information Bar. The first three are part of the CGID_DocHostCommandHandler group. Hosting applications should implement IOleCommandTarget on their implementation of IDocHostUIHandler to receive IOleCommandTarget::Exec calls from the WebBrowser Control.
- OLECMDID_PAGEACTIONBLOCKED
- OLECMDID_PAGEACTIONUIQUERY
- OLECMDID_FOCUSVIEWCONTROLS
Hosting applications can use the following two new OLECMDID commands to make IOleCommandTarget::Exec calls on the WebBrowser Control.
- OLECMDID_FOCUSVIEWCONTROLSQUERY
- OLECMDID_SHOWPAGEACTIONMENU
POINT pt = { 0 };
GetCursorPos(&pt);
CComVariant varHwnd((LONG)hwnd);
CComVariant varX(pt.x);
CComVariant varY(pt.y);
SAFEARRAY* psa = SafeArrayCreateVector(VT_VARIANT, 0, 3);
LONG lIndex = 0;
SafeArrayPutElement(psa, &lIndex, &varHwnd);
lIndex++;
SafeArrayPutElement(psa, &lIndex, &varX);
lIndex++;
SafeArrayPutElement(psa, &lIndex, &varY);
CComVariant varArgIn;
V_VT(&varArgIn) = VT_ARRAY | VT_I4;
V_ARRAY(&varArgIn) = psa;
pBrowser-<>*Fix pointer operator!!ExecWB(OLECMDID_SHOWPAGEACTIONMENU, (OLECMDEXECOPT)dwPageActionFlags, &varArgIn, NULL);
Also, an application can override the default security zone settings by implementing IInternetSecurityManager. See Implementing a Custom Security Manager for more information.
Conclusion
You now have a number of techniques at your disposal to customize the WebBrowser Control. This article is by no means exhaustive, but hopefully you also now have a sense of some of the possibilities you may discover on your own beyond the techniques in this article. Check out the Internet Explorer registry to see the kinds of information stored there that you can modify with IDocHostUIHandler::GetOptionKeyPath or IDocHostUIHandler2::GetOverrideKeyPath. Keep in mind that many registry settings are interdependent with others. You may have to do some experimentation to discover how particular registry settings can be customized effectively. You can also look into IDocHostUIHandler::GetDropTarget if you want to control what the WebBrowser Control does during drag-and-drop operations.
Part one of this tutorial introduced several of the standard hosting interfaces for the WebBrowser Control: IDocHostUIHandler, IDocHostShowUI, and ICustomDoc to name a few. Part two of this article explains how to retrieve a reference to the client site while processing IPersistMoniker::Load, and how to use the IServiceProvider and IOleCommandTarget interfaces to send messages between the Active document (DocObject) and its host.
IPersistMoniker and IBindCtx
When the WebBrowser Control determines that a hyperlinked resource is associated with a helper application, it creates an instance of the target application and prompts the Active document (DocObject) to load the resource by using the IPersistMoniker interface. Before the browser navigates to a document, it registers a reference to its IOleClientSite interface in the binding context using IBindCtx::RegisterObjectParam. This reference allows the DocObject to retrieve and use the client site during IPersistMoniker::Load.
The following example demonstrates how to retrieve and set the client site from the binding context:
HRESULT CDoc::Load(BOOL fFullyAvailable, IMoniker *pimkName, LPBC pibc, DWORD grfMode)
{
HRESULT hr = S_OK;
CComPtrspUnk;
CComPtrspBindCtx;
// SHDOCVW stashes the client site in the bind context.
if (pibc)
{
pibc->GetObjectParam(SZ_HTML_CLIENTSITE_OBJECTPARAM, &spUnk);
if (spUnk)
{
CComQIPtrspSite(spUnk);
if (spSite)
{
hr = SetClientSite(spSite);
}
}
}
}
IServiceProvider
The IServiceProvider interface is a generic access mechanism to locate a GUID-identified service that is provided through a control or any other object. Using IServiceProvider::QueryService, its one and only method, the caller specifies a service identifier (SID), the IID of the desired interface, and the address of the interface pointer variable that is set upon a successful return.
In many ways, IServiceProvider::QueryService functions a lot like QueryInterface. However, the more adaptable IServiceProvider mechanism allows an implementer to delegate the query to one of its member objects, or to hand off the request to a callback hierarchy that is searched for an object that can support the requested IID. The implementer need not recognize the interfaces being requested. In common usage, IServiceProvider::QueryService is used to improve the discoverability of some interfaces.
QueryService Example
For example, a call to QueryService with SID_SShellBrowser gets the nearest shell browser, such as the Microsoft Internet Explorer Web Object Control (WebOC). So, if the DocObject is hosted in the search pane, it will not mistakenly execute commands on the main browser window. SID_SShellBrowser is a WebOC-specific service; any DocObject that implements the IBrowserService interface should respond to queries for this service.
The following example demonstrates this basic scenario.
CComPtrpSvc;
CComPtrpShb;
HRESULT hr;
hr = m_pUnk->QueryInterface(IID_IServiceProvider, &pSvc);
if (S_OK == hr && pSvc)
{
hr = pSvc->QueryService(SID_SShellBrowser, IID_IShellBrowser, &pShb);
if (hr == E_NOINTERFACE)
return;
// . . . use the interface here
pShb->Release();
}
CLSID_CMarkup
The special CLSID_CMarkup GUID is used to determine if an object is a native MSHTML markup object. No code except Internet Explorer should use this CLSID. Your QueryService or QueryInterface implementation should return E_NOINTERFACE if invoked with this GUID.
PRIVATE_GUID(CLSID_CMarkup, 0x3050F4FB, 0x98B5, 0x11CF, 0xBB, 0x82, 0x00, 0xAA, 0x00, 0xBD, 0xCE, 0x0B);
IOleCommandTarget
The IOleCommandTarget interface enables objects and their containers to dispatch commands to each other. Available commands are defined by integer identifiers from a command group, which is itself identified by command group ID (also a GUID). The interface allows a caller both to query for one or more supported commands within a group and to issue a command to the object.
The WebBrowser Control uses the IOleCommandTarget interface extensively. The following sections highlight just a few of the ways that the client site can communicate with its control.
Showing a Certificate Dialog
With Internet Explorer 6 for Microsoft Windows XP Service Pack 2 (SP2) and later, you can show the Certificate dialog when the user is viewing a valid Secure Hypertext Transfer Protocol (HTTPS) site. This is the same as the user clicking the lock icon in Internet Explorer. You can use the DWebBrowserEvents2::SetSecureLockIcon event to show your own icon.
#define SHDVID_SSLSTATUS 33
IOleCommandTarget *pct;
if (SUCCEEDED(pWebBrowser2->QueryInterface(IID_IOleCommandTarget, (void **) &pct)))
{
pct->Exec(&CGID_ShellDocView, SHDVID_SSLSTATUS, 0, NULL, NULL);
pct->Release();
}
Controlling Navigation (Revisited)
If the MSHTML control is aggregated, the controlling DocObject is in a position to regulate navigation events. The fact that a document can navigate on its own implies that it will also take care of updating the navigation history.
In Internet Explorer 6 and later, the DocObject can indicate to the client site that it can navigate using CGID_DocHostCmdPriv (a privately defined command group GUID) and the DOCHOST_DOCCANNAVIGATE command. A pointer to the object that implements the IHTMLWindow2 interface is passed with the command in the VARIANTARG* parameter pvaIn. (Set pvaIn to NULL if the document cannot perform its own navigation.)
DEFINE_SHLGUID( CGID_DocHostCmdPriv, 0x000214d4, 0, 0);
#define DOCHOST_DOCCANNAVIGATE 0
// Create variant, initialize with pointer to our IUnknown
VARIANTARG var;
V_VT(&var) = VT_UNKNOWN;
V_UNKNOWN(&var) = (IUnknown*)this;
// Execute IOleCommandTarget with command group and ID
m_pCmdTarg->Exec(&CGID_DocHostCmdPriv, DOCHOST_DOCCANNAVIGATE, 0L, &var, NULL);
Specifying the Zone Icon
In conjunction with CGID_Explorer, the SBCMDID_MIXEDZONE command is used to determine the zone icon and text displayed in the Internet Explorer status bar.
Active document objects hosted in Internet Explorer 6 for Windows XP SP2 or later can respond to the SBCMDID_MIXEDZONE command to determine the zone icon and text displayed in the Internet Explorer status bar. The document object should implement IOleCommandTarget, and Internet Explorer calls IOleCommandTarget::Exec with the CGID_Explorer command group.
To specify the zone icon:
Define SBCMDID_MIXEDZONE.
#define SBCMDID_MIXEDZONE 39
Initialize pvaOut with VariantInit.
Determine the zone you wish to specify and then set pvaOut as follows. To specify:
- Internet, Intranet, Trusted or Untrusted zones; set pvaOut->vt to VT_UI4, and set pvaOut->ulVal to URLZONE_INTERNET, URLZONE_INTRANET, URLZONE_TRUSTED or URLZONE_UNTRUSTED respectively.
- A mixed zone, set pvaOut->vt to VT_NULL.
- An unknown zone, set pvaOut->vt to VT_EMPTY.
If an active document does not handle SBCMDID_MIXEDZONE, the default behavior is to show Unknown Zone in the status bar.
More Examples of Commands
The IDM_CONTEXT command (CGID_EditStateCommands command group) is used to determine if editing command should be routed to the host first. An IOleCommandTarget implementation will return S_OK to indicate that editing commands should be routed.
If the host is designed to handle editing commands, it will respond to the SID_SEditCommandTarget service identifier by providing an IOleCommandTarget interface to process editing commands as defined in the CGID_MSHTML command group.
DEFINE_GUID(CGID_MSHTML, 0xde4ba900, 0x59ca,0x11cf, 0x95, 0x92, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00);
For more information on editing commands, see Introduction to MSHTML Editing and MSHTML Command Identifiers reference.
Context Menus and Extensions
The IDocHostUIHandler::ShowContextMenu method was introduced in WebBrowser Customization (Part 1). The following examples demonstrate how to use the IOleCommandTarget interface with SHDVID_GETMIMECSETMENU and SHDVID_ADDMENUEXTENSIONS (CGID_ShellDocView command group) to add and remove custom menu options from the standard Internet Explorer context menu.
The WebBrowser Control gets its context menu resources from Shdoclc.dll. That knowledge and a few #defines gives you a chance to manipulate the browser's menu. Let's say for instance that you're happy with the default menu, except for the View Source option, which you'd like to eliminate. The following code loads the WebBrowser Control shortcut menu resource from Shdoclc.dll, chooses the correct menu for the context, removes the menu item corresponding to the IDM_VIEWSOURCE command, and then displays the menu.
HRESULT CBrowserHost::ShowContextMenu(DWORD dwID,
POINT *ppt,
IUnknown *pcmdTarget,
IDispatch *pdispObject)
{
#define IDR_BROWSE_CONTEXT_MENU 24641
#define IDR_FORM_CONTEXT_MENU 24640
#define SHDVID_GETMIMECSETMENU 27
#define SHDVID_ADDMENUEXTENSIONS 53
HRESULT hr;
HINSTANCE hinstSHDOCLC;
HWND hwnd;
HMENU hMenu;
CComPtrspCT;
CComPtrspWnd;
MENUITEMINFO mii = {0};
CComVariant var, var1, var2;
hr = pcmdTarget->QueryInterface(IID_IOleCommandTarget, (void**)&spCT);
hr = pcmdTarget->QueryInterface(IID_IOleWindow, (void**)&spWnd);
hr = spWnd->GetWindow(&hwnd);
hinstSHDOCLC = LoadLibrary(TEXT("SHDOCLC.DLL"));
if (hinstSHDOCLC == NULL)
{
// Error loading module -- fail as securely as possible
return;
}
hMenu = LoadMenu(hinstSHDOCLC,
MAKEINTRESOURCE(IDR_BROWSE_CONTEXT_MENU));
hMenu = GetSubMenu(hMenu, dwID);
// Get the language submenu
hr = spCT->Exec(&CGID_ShellDocView, SHDVID_GETMIMECSETMENU, 0, NULL, &var);
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_SUBMENU;
mii.hSubMenu = (HMENU) var.byref;
// Add language submenu to Encoding context item
SetMenuItemInfo(hMenu, IDM_LANGUAGE, FALSE, &mii);
// Insert Shortcut Menu Extensions from registry
V_VT(&var1) = VT_INT_PTR;
V_BYREF(&var1) = hMenu;
V_VT(&var2) = VT_I4;
V_I4(&var2) = dwID;
hr = spCT->Exec(&CGID_ShellDocView, SHDVID_ADDMENUEXTENSIONS, 0, &var1, &var2);
// Remove View Source
DeleteMenu(hMenu, IDM_VIEWSOURCE, MF_BYCOMMAND);
// Show shortcut menu
int iSelection = ::TrackPopupMenu(hMenu,
TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,
ppt->x,
ppt->y,
0,
hwnd,
(RECT*)NULL);
// Send selected shortcut menu item command to shell
LRESULT lr = ::SendMessage(hwnd, WM_COMMAND, iSelection, NULL);
FreeLibrary(hinstSHDOCLC);
return S_OK;
}
Appending Extensions to Your Custom Menu
In the previous example, the menu (hMenu) and menu context identifier (dwID) are passed as arguments to the IOleCommandTarget with the SHDVID_ADDMENUEXTENSIONS command. This operation inserts the registered menu extensions into the menu.
Should you choose to replace the standard menu with your own, you can still append menu extensions to your custom menu. Simply include a blank IDM_MENUEXT_PLACEHOLDER menu option in your menu definition to indicate where the custom commands are to be inserted. Menu extensions are inserted just before this placeholder. You can also add your own custom command to the standard menu by inserting the menu option before IDM_MENUEXT_PLACEHOLDER, as shown in the following example.
#define IDM_MENUEXT_PLACEHOLDER 6047
// If the placeholder is gone or was never there, then just exit
if (GetMenuState(hMenu, IDM_MENUEXT_PLACEHOLDER, MF_BYCOMMAND) != (UINT) -1)
{
InsertMenu(hMenu, // The Context Menu
IDM_MENUEXT_PLACEHOLDER, // The item to insert before
MF_BYCOMMAND|MF_STRING, // by item ID and str value
IDM_MENUEXT_FIRST__ + nExtCur, // the command ID
(LPTSTR)aOpts[nExtCur].pstrText);// The menu command text
// Remove placeholder
DeleteMenu(hMenu, IDM_MENUEXT_PLACEHOLDER, MF_BYCOMMAND);
}
The menu IDs for extensions fall between IDM_MENUEXT_FIRST__ and IDM_MENUEXT_LAST__ for a maximum of 33 custom commands.
References
The following table indicates where the identifiers described in this article are defined:
Identifier | Header File |
---|---|
CGID_EditStateCommands | Mshtml.h |
CGID_Explorer | Shlguid.h |
CGID_MSHTML | Mshtmhst.h |
CGID_ShellDocView | Shlguid.h |
IDM_CONTEXT | Mshtmcid.h |
SID_SEditCommandTarget | Mshtml.h |
SID_SShellBrowser | Shlguid.h |
SZ_HTML_CLIENTSITE_OBJECTPARAM | Mshtmhst.h |
原文链接:http://msdn.microsoft.com/workshop/browser/hosting/wbcustomization.asp