在使用CHtmlView的程序中处理NewWindow3和ShowModalDialog

截至MFC 9.0(Visual Studio 2008)Beta1版本为止CHtmlView还不支持Windows XP SP2中IE新增的NewWindow3事件,但是在atlmfc/src/viewhtml.cpp源代码里面已经有了其他事件的处理代码,要加上NewWindow3的支持也很简单。

添加下面几行到你对CHtmlView的派生类的声明内 (在此示例中姑且名为CHtmlViewTestView)

void NewWindow3(
IDispatch **ppDisp,
VARIANT_BOOL *Cancel,
DWORD dwFlags,
BSTR bstrUrlContext,
BSTR bstrUrl
);

DECLARE_EVENTSINK_MAP()

添加下面几行到你对CHtmlViewTestView的实现源文件

#include <exdisp.h> //For IWebBrowser2* and others
#include <exdispid.h>
#include <Mshtml.h>
#include <Mshtmdid.h>
#include <shobjidl.h>

BEGIN_EVENTSINK_MAP(CHtmlViewTestView, CHtmlView)
ON_EVENT(CHtmlViewTestView, AFX_IDW_PANE_FIRST,DISPID_NEWWINDOW3,NewWindow3,VTS_PDISPATCH VTS_PBOOL VTS_I4 VTS_BSTR VTS_BSTR)
END_EVENTSINK_MAP()

void CHtmlViewTestView::NewWindow3(
IDispatch **ppDisp,
VARIANT_BOOL *Cancel,
DWORD dwFlags,
BSTR bstrUrlContext,
BSTR bstrUrl
)
{
CDocTemplate* pDocTemplate=GetDocument()->GetDocTemplate();
CDocument* pDocument=pDocTemplate->OpenDocumentFile(NULL);
POSITION pos= pDocument->GetFirstViewPosition();
CHtmlViewTestView* pNewView=(CHtmlViewTestView*)pDocument->GetNextView(pos);
pNewView->SetRegisterAsBrowser(TRUE);
*ppDisp=pNewView->GetApplication();
}

在CHtmlView派生类里面处理ShowHtmlDialog比较麻烦一点,需要扩展控件站点,但是MFC对控件站点的扩展是不可复用的。MFC里面的两个浏览器控件封装类 CHtmlView和CDHtmlDialog甚至没有共享任何代码,而是使用几乎相同的代码来做完全一样的事情:重定向IDocHostUIHandler 的方法。CHtmlView用的扩展叫做 CHtmlControlSite,甚至都不在MFC头文件里面,而CDHtmlDialog用的 CBrowserControlSite在afxdhtml.h这个文件里面,暴露了GetInterfaceHook,留下了一点扩展的空间。

现在回到CHtmlView。要创建一个控件站点的扩展,你需要重载CWnd::CreateControlSite。这个函数是在MFC 7.0里面为扩展浏览器空间量身定做的,但是在MFC 8.0里面也被用于嵌入.Net里面的Windows Forms控件。

BOOL CHtmlViewTestView::CreateControlSite(COleControlContainer* pContainer,
COleControlSite** ppSite, UINT /* nID */, REFCLSID /* clsid */)
{
ASSERT(ppSite != NULL);
*ppSite = new CExtendedHtmlControlSite(pContainer,this);
return TRUE;
}

实际上,pContainer->m_pWnd 就是第二个参数this (CHtmlViewTestView),,所以我应该可以省下一个参数,把pContainer->m_pWnd 转换成CHtmlViewTestView;但是在我写这个类的时候还不知道这一点。

控件站点的扩展需要扩展COleControlSite,一个在MFC 6.0里面未公开的类,但是在MFC 7.0里面被公开了,用来支持类级别的控件站点扩展。在这之前,你只能够调用AfxEnableControlContainer来替换全局的控件容器工厂。

class CExtendedHtmlControlSite :
public COleControlSite
{
public:
CExtendedHtmlControlSite(COleControlContainer* pContainer,CHtmlViewTestView* pView);
virtual ~CExtendedHtmlControlSite(void);
protected:
CHtmlViewTestView* m_pView;
}

CExtendedHtmlControlSite::CExtendedHtmlControlSite(COleControlContainer* pContainer,CHtmlViewTestView* pView)
:COleControlSite(pContainer),m_pView(pView)
{
}

CExtendedHtmlControlSite::~CExtendedHtmlControlSite(void)
{
}

这里m_pView被存下来用于重定向INewWindowManager的方法到CHtmlViewTestView类.。

现在说到有趣的部分了。实际上,浏览器控件并不查询控件站点扩展的INewWindowManager接口,而是调用控件站点扩展对IServiceProvider::QueryService的实现。所以我需要先实现IServiceProvider,在浏览器控件进行服务查询时返回我自己的INewWindowManager。

BEGIN_INTERFACE_PART(ServiceProvider, IServiceProvider)
STDMETHOD(QueryService)(REFGUID,REFIID,void**);
END_INTERFACE_PART(ServiceProvider)

BEGIN_INTERFACE_PART(NewWindowManager, INewWindowManager)
STDMETHOD(EvaluateNewWindow)(
LPCWSTR pszUrl,
LPCWSTR pszName,
LPCWSTR pszUrlContext,
LPCWSTR pszFeatures,
BOOL fReplace,
DWORD dwFlags,
DWORD dwUserActionTime);
END_INTERFACE_PART(NewWindowManager);

ULONG FAR EXPORT CExtendedHtmlControlSite::XServiceProvider::AddRef()
{
METHOD_PROLOGUE(CExtendedHtmlControlSite, ServiceProvider)
return pThis->ExternalAddRef();
}

ULONG FAR EXPORT CExtendedHtmlControlSite::XServiceProvider::Release()
{
METHOD_PROLOGUE(CExtendedHtmlControlSite, ServiceProvider)
return pThis->ExternalRelease();
}

HRESULT FAR EXPORT CExtendedHtmlControlSite::XServiceProvider::QueryInterface(REFIID riid,
void** ppvObj)
{
METHOD_PROLOGUE(CExtendedHtmlControlSite, ServiceProvider)
HRESULT hr = (HRESULT)pThis->ExternalQueryInterface(&riid, ppvObj);
return hr;
}
STDMETHODIMP CExtendedHtmlControlSite::XServiceProvider::QueryService(REFGUID guidService,
REFIID riid,
void** ppvObject)
{
if (riid == IID_INewWindowManager)
{
METHOD_PROLOGUE(CExtendedHtmlControlSite, ServiceProvider);
HRESULT hr = (HRESULT)pThis->ExternalQueryInterface(&riid, ppvObject);
return hr;
}
else
{
*ppvObject = NULL;

}
return E_NOINTERFACE;
}

ULONG CExtendedHtmlControlSite::XNewWindowManager::AddRef()
{
METHOD_PROLOGUE(CExtendedHtmlControlSite, NewWindowManager);

return pThis->ExternalAddRef();
}

ULONG CExtendedHtmlControlSite::XNewWindowManager::Release()
{
METHOD_PROLOGUE(CExtendedHtmlControlSite, NewWindowManager);

return pThis->ExternalRelease();
}

HRESULT CExtendedHtmlControlSite::XNewWindowManager::QueryInterface(REFIID riid, void ** ppvObj)
{
METHOD_PROLOGUE(CExtendedHtmlControlSite, NewWindowManager);

return pThis->ExternalQueryInterface( &riid, ppvObj );
}

HRESULT CExtendedHtmlControlSite::XNewWindowManager::EvaluateNewWindow(
LPCWSTR pszUrl,
LPCWSTR pszName,
LPCWSTR pszUrlContext,
LPCWSTR pszFeatures,
BOOL fReplace,
DWORD dwFlags,
DWORD dwUserActionTime
)
{
METHOD_PROLOGUE(CExtendedHtmlControlSite, NewWindowManager);

return pThis->m_pView->EvaluateNewWindow(
pszUrl,
pszName,
pszUrlContext,
pszFeatures,
fReplace,
dwFlags,
dwUserActionTime);
}

实际上,我可以在另外一个类中实现INewWindowManager,然后在服务查询中返回该类的一个对象,但是由于INewWindowManager只被用来扩展浏览器控件的控件站点,这个单独的INewWindowManager实现并不能被重用。

最后,为了让CHtmlView的IDocHostUIHandler方法继续工作,我需要重定向IDocHostUIHandler的方法:

DECLARE_INTERFACE_MAP()

BEGIN_INTERFACE_PART(DocHostUIHandler, IDocHostUIHandler)
STDMETHOD(ShowContextMenu)(DWORD, LPPOINT, LPUNKNOWN, LPDISPATCH);
STDMETHOD(GetHostInfo)(DOCHOSTUIINFO*);
STDMETHOD(ShowUI)(DWORD, LPOLEINPLACEACTIVEOBJECT,
LPOLECOMMANDTARGET, LPOLEINPLACEFRAME, LPOLEINPLACEUIWINDOW);
STDMETHOD(HideUI)(void);
STDMETHOD(UpdateUI)(void);
STDMETHOD(EnableModeless)(BOOL);
STDMETHOD(OnDocWindowActivate)(BOOL);
STDMETHOD(OnFrameWindowActivate)(BOOL);
STDMETHOD(ResizeBorder)(LPCRECT, LPOLEINPLACEUIWINDOW, BOOL);
STDMETHOD(TranslateAccelerator)(LPMSG, const GUID*, DWORD);
STDMETHOD(GetOptionKeyPath)(OLECHAR **, DWORD);
STDMETHOD(GetDropTarget)(LPDROPTARGET, LPDROPTARGET*);
STDMETHOD(GetExternal)(LPDISPATCH*);
STDMETHOD(TranslateUrl)(DWORD, OLECHAR*, OLECHAR **);
STDMETHOD(FilterDataObject)(LPDATAOBJECT , LPDATAOBJECT*);
END_INTERFACE_PART(DocHostUIHandler)

STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::GetExternal(LPDISPATCH *lppDispatch)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->m_pView->OnGetExternal(lppDispatch);
}STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::ShowContextMenu(
DWORD dwID, LPPOINT ppt, LPUNKNOWN pcmdtReserved, LPDISPATCH pdispReserved)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->m_pView->OnShowContextMenu(dwID, ppt, pcmdtReserved, pdispReserved);
}STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::GetHostInfo(
DOCHOSTUIINFO *pInfo)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->m_pView->OnGetHostInfo(pInfo);
}STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::ShowUI(
DWORD dwID, LPOLEINPLACEACTIVEOBJECT pActiveObject,
LPOLECOMMANDTARGET pCommandTarget, LPOLEINPLACEFRAME pFrame,
LPOLEINPLACEUIWINDOW pDoc)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->m_pView->OnShowUI(dwID, pActiveObject, pCommandTarget, pFrame, pDoc);
}STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::HideUI(void)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)

return pThis->m_pView->OnHideUI();
}
STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::EnableModeless(BOOL fEnable)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->m_pView->OnEnableModeless(fEnable);
}STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::OnDocWindowActivate(BOOL fActivate)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->m_pView->OnDocWindowActivate(fActivate);
}STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::OnFrameWindowActivate(
BOOL fActivate)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->m_pView->OnFrameWindowActivate(fActivate);
}

STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::ResizeBorder(
LPCRECT prcBorder, LPOLEINPLACEUIWINDOW pUIWindow, BOOL fFrameWindow)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->m_pView->OnResizeBorder(prcBorder, pUIWindow, fFrameWindow);
}
STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::TranslateAccelerator(
LPMSG lpMsg, const GUID* pguidCmdGroup, DWORD nCmdID)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->m_pView->OnTranslateAccelerator(lpMsg, pguidCmdGroup, nCmdID);
}
STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::GetOptionKeyPath(
LPOLESTR* pchKey, DWORD dwReserved)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->m_pView->OnGetOptionKeyPath(pchKey, dwReserved);
}STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::GetDropTarget(
LPDROPTARGET pDropTarget, LPDROPTARGET* ppDropTarget)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->m_pView->OnGetDropTarget(pDropTarget, ppDropTarget);
}

STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::TranslateUrl(
DWORD dwTranslate, OLECHAR* pchURLIn, OLECHAR** ppchURLOut)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->m_pView->OnTranslateUrl(dwTranslate, pchURLIn, ppchURLOut);
}STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::FilterDataObject(
LPDATAOBJECT pDataObject, LPDATAOBJECT* ppDataObject)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->m_pView->OnFilterDataObject(pDataObject, ppDataObject);
}
STDMETHODIMP_(ULONG) CExtendedHtmlControlSite::XDocHostUIHandler::AddRef()
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->ExternalAddRef();
}
STDMETHODIMP_(ULONG) CExtendedHtmlControlSite::XDocHostUIHandler::Release()
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->ExternalRelease();
}

STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::QueryInterface(
REFIID iid, LPVOID far* ppvObj)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)
return pThis->ExternalQueryInterface(&iid, ppvObj);
}STDMETHODIMP CExtendedHtmlControlSite::XDocHostUIHandler::UpdateUI(void)
{
METHOD_PROLOGUE_EX_(CExtendedHtmlControlSite, DocHostUIHandler)

return pThis->m_pView->OnUpdateUI();
}

终于干完了,现在你可以捕获ShowModalDialog了

HRESULT CHtmlViewTestView::EvaluateNewWindow(
LPCWSTR pszUrl,
LPCWSTR pszName,
LPCWSTR pszUrlContext,
LPCWSTR pszFeatures,
BOOL fReplace,
DWORD dwFlags,
DWORD dwUserActionTime
)
{
CString url(pszUrl);
if(url.MakeLower().Find(_T("showdialogtest.htm"))!=-1)
{
return S_FALSE;//block the new window
}
return E_FAIL;//default
}

你可以在这里任意加入你喜欢的策略。

这篇文章应该足够作为浏览器控件扩展基础。如果你需要加入更多的扩展,例如IDocHostUIHandler2、 IInternetSecurityManager、 IDocHostShowUI、 IOleCommandTarget或者IAuthenticate,像文章里面添加接口组件,如果有必要的话,在浏览器控件进行服务查询时返回扩展的接口就可以了。

你可能感兴趣的:(showModalDialog)