Chinaunix首页 | 论坛 | 博客
  • 博客访问: 625347
  • 博文数量: 133
  • 博客积分: 1566
  • 博客等级: 上尉
  • 技术积分: 1230
  • 用 户 组: 普通用户
  • 注册时间: 2010-12-01 09:31
文章分类

全部博文(133)

文章存档

2019年(1)

2018年(1)

2017年(8)

2016年(9)

2015年(17)

2014年(4)

2013年(31)

2012年(25)

2011年(36)

2010年(1)

我的朋友

分类: Windows平台

2013-05-14 11:10:31

截至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 //For IWebBrowser2* and others
#include
#include
#include
#include

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,像文章里面添加接口组件,如果有必要的话,在浏览器控件进行服务查询时返回扩展的接口就可以了。

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