使用C++实现SDK之WebBrowser容器
2016-06-08 18:17
513 查看
转:http://blog.csdn.net/norsd/article/details/2921389
一.由来
我还记还得当自认为学习完了C++语法后, 兴致勃勃的打开MFC向导,开始所谓"MFC高级自动化编程"时, 我不由喊道: 这他妈的都是些什么乱七八糟的东西啊.诚然,MFC为了方便愚蠢的程序员,的确是让人瞬间高级编程, 可是愚蠢的我们还是会用CString strTest;写出(PWSTR)strTest.GetBuffer() 这种自作聪明的用法,结果就是整个程序好像发了疯.
终于有一天,我大喊一声: 滚蛋吧,MFC!
太阳当空照,上帝对 我笑: 你Y去写个有WebBrowser控件的窗口吧
二.结构
MFC的HTMLDialog 是一个魔盒, 他容易上手,可是却很难深入,究其缘由, 私以为无外乎MFC包装的太好, 不识庐山真面目,只缘身在此山中. 总是以为作为C类型的程序员,喜欢深入原理是职业特性.
于是首先看到有强人写的纯C实现WebBrowser控件,他在这里: http://www.codeproject.com/KB/COM/cwebpage.aspx 这位同志很好的展示了在没有C++的支持下,支持ActiveX是多么痛苦的一件事情...........汗.
在下觉得,撇开C++的类的特性,虚拟特性,重载特性,而去用纯C实现,实在有点过犹不及,当然这种练习修炼内功很好,但是实际应用上显得麻烦.毕竟,ActiveX很好的使用了C++的特性,而不是C的特性.
接着,开始使用纯SDK编写容器. 读者可以搜索csdn中关于SDK实现WebBrowser容器的帖子, 许多人说那是非常繁琐复杂.所谓人云亦云,各位看官不妨跟着我一试,且看到底何如?
WebBrowser容器的实现需要许多接口, 也许正是这吓退了许多人, 实际情况是,许多接口的方法没几个需要实现,大部分只需要直接返回E_NOTIMPL和S_OK, E_FAIL.
让我们命名我们编写的容器叫 WebBrowser (可能名字有点糊涂,但是因为源代码中就是这个名字,所以就不改了,大家只需要注意,这个是一个WebBrowser控件的容器 ), 在实现这个容器后, 我们把他作为一个窗口类的父类,这样 就能实现WebBrowser的页面窗口.
首先一个WB( WebBrowser) 容器需要以下接口:
public IDispatch
public IOleClientSite
public IOleInPlaceSite
public IOleInPlaceFrame
每个接口需要实现的方法分别是:
// IUnknown methods
virtual STDMETHODIMP QueryInterface(REFIID iid,void**ppvObject);
virtual STDMETHODIMP_(ULONG) AddRef();
virtual STDMETHODIMP_(ULONG) Release();
// IDispatch Methods
HRESULT _stdcall GetTypeInfoCount(unsigned int * pctinfo);
HRESULT _stdcall GetTypeInfo(unsigned int iTInfo,LCID lcid,ITypeInfo FAR* FAR* ppTInfo);
HRESULT _stdcall GetIDsOfNames(REFIID riid,OLECHAR FAR* FAR* rgszNames,unsigned int cNames,LCID lcid,DISPID FAR* rgDispId);
HRESULT _stdcall Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS FAR* pDispParams,VARIANT FAR* pVarResult,EXCEPINFO FAR* pExcepInfo,unsigned int FAR* puArgErr);
// IOleClientSite methods
virtual STDMETHODIMP SaveObject();
virtual STDMETHODIMP GetMoniker(DWORD dwA,DWORD dwW,IMoniker**pm);
virtual STDMETHODIMP GetContainer(IOleContainer**pc);
virtual STDMETHODIMP ShowObject();
virtual STDMETHODIMP OnShowWindow(BOOL f);
virtual STDMETHODIMP RequestNewObjectLayout();
// IOleInPlaceSite methods
virtual STDMETHODIMP GetWindow(HWND *p);
virtual STDMETHODIMP ContextSensitiveHelp(BOOL);
virtual STDMETHODIMP CanInPlaceActivate();
virtual STDMETHODIMP OnInPlaceActivate();
virtual STDMETHODIMP OnUIActivate();
virtual STDMETHODIMP GetWindowContext(IOleInPlaceFrame** ppFrame,IOleInPlaceUIWindow **ppDoc,LPRECT r1,LPRECT r2,LPOLEINPLACEFRAMEINFO o);
virtual STDMETHODIMP Scroll(SIZE s);
virtual STDMETHODIMP OnUIDeactivate(int);
virtual STDMETHODIMP OnInPlaceDeactivate();
virtual STDMETHODIMP DiscardUndoState();
virtual STDMETHODIMP DeactivateAndUndo();
virtual STDMETHODIMP OnPosRectChange(LPCRECT);
// IOleInPlaceFrame methods
virtual STDMETHODIMP GetBorder(LPRECT l);
virtual STDMETHODIMP RequestBorderSpace(LPCBORDERWIDTHS);
virtual STDMETHODIMP SetBorderSpace(LPCBORDERWIDTHS w);
virtual STDMETHODIMP SetActiveObject(IOleInPlaceActiveObject*pV,LPCOLESTR s);
virtual STDMETHODIMP InsertMenus(HMENU h,LPOLEMENUGROUPWIDTHS x);
virtual STDMETHODIMP SetMenu(HMENU h,HOLEMENU hO,HWND hw);
virtual STDMETHODIMP RemoveMenus(HMENU h);
virtual STDMETHODIMP SetStatusText(LPCOLESTR t);
virtual STDMETHODIMP EnableModeless(BOOL f);
virtual STDMETHODIMP TranslateAccelerator(LPMSG,WORD);
这里插几句题外话, 我讨厌看文章, 因为萝莉罗嗦也没看到那个爆炸点, 就是你看到他,一下子思路有了头绪, 不再是一头雾水了.可惜,我看到的大部分文章都不是这样, 为了避免这个问题, 我在这里放个屁, 嗯,也算是一个小小的爆破吧.
好,上面已经罗列了需要的接口和接口的方法.
要想实现WB的容器,你必须有这段代码:
WebBrowser::WebBrowser()
{
//初始化OLE
OleInitialize(0);
//创建IStorage对象,其中_pStorage是WebBrowser的私有变量
StgCreateDocfile(0,STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_CREATE,0,&_pStorage);
//创建IOleObject对象,我们看到,短短的一条语句,已经几乎创建完成了WebBrowser!
//this在这里时表示当前类(即WebBrowser)作为容器,承载_pOleObj这个控件
//这里就是放屁现场了,大家好好看看哈.
OleCreate( CLSID_WebBrowser,IID_IOleObject,OLERENDER_DRAW,
0 , this, _pStorage, (void**)&_pOleObj );
//获得IOleInPlaceObject对象
_pOleObj->QueryInterface(IID_IOleInPlaceObject,(LPVOID*)&_pInPlaceObj);
}
上面一段代码已经创建了wb的Ole对象, 接下来,你只需要打开WB,然后打开网页.我们用两个函数实现,他们是OpenWebBrowser() 和 OpenURL( VARIANT* pVarUrl )
OpenWebBrowser:
BOOL
WebBrowser::OpenWebBrowser()
{
BOOL bRet = FALSE;
// RECTWIDTH 和 RECTHEIGHT 是计算RECT长宽的宏函数
// _rcWebWnd 是WebBrowser的私有RECT成员,就是GetHWND()的大小.
// 下面的语句就是关联两者
// GetHWND()获取容器的框架窗口句柄,就是我们创建的窗口,关于这个函数,下面会单独讲
if( (RECTWIDTH(_rcWebWnd) && RECTHEIGHT(_rcWebWnd)) == 0 )
::GetClientRect( GetHWND() ,&_rcWebWnd);//设置WebBrowser的大小为窗口的客户区大小.
// _bInPlaced 是WebBrowser的一个私有BOOL成员,初始为false
// 一旦执行了OLEIVERB_INPLACEACTIVATE (就是下面的操作)后,立即设置为true,防止控件被多次放置在容器中
// _GetOleObject()是WebBrowser的一个保护函数成员,他只是简单的返回_pOleObj
// _pOleObj就是WB控件本身. 考虑到WebBrowser是一个基本类,以后必然被其他类继承,所以使用_GetOleObject()来返回_pOleObject,来实现防止_pOleObject本身不被修改的意外.
if( _bInPlaced == false )// Activate In Place
{
_bInPlaced = true;//_bInPlaced must be set as true, before INPLACEACTIVATE, otherwise, once DoVerb, it would return error;
_bExternalPlace = 0;//lParam;
_GetOleObject()->DoVerb(OLEIVERB_INPLACEACTIVATE,0,this,0, GetHWND(),&_rcWebWnd);
_bInPlaced = true;
}
bRet = TRUE;
RETURN:
return bRet;
}
OpenURL(...)
BOOL
WebBrowser::OpenURL(VARIANT* pVarUrl)
{
BOOL bRet = FALSE;
//GetWebBrowser2 返回IWebBrowser2
//他的实现是这样的:
//if( _pWB2 != NULL )//_pWB2是WebBrowser的私有IWebBrowser2指针
// return _pWB2;//如果_pWB2已经不是NULL,即已经获取过内容,则直接返回
////否则使用WB控件对象枚举IWebBrowser2指针
//_pOleObj->QueryInterface(IID_IWebBrowser2,(void**)&_pWB2);
//return _pWB2;
GetWebBrowser2()->Navigate2( pVarUrl,0,0,0,0);//打开网页
bRet = TRUE;
RETURN:
return bRet;
}
三.细节
前面说过,那几个接口的方法,大部分都是直接返回E_NOTIMPL和S_OK, E_FAIL.,我说大部分,说明必然有些函数需要做一些事情,而且其作用很不小, 请看官别马虎了这段.
:)
IDispatch 接口中的 IUnknown 的实现实在是不必多言, 稍微说一下接口枚举:
STDMETHODIMP WebBrowser::QueryInterface(REFIID iid,void**ppvObject)
{
*ppvObject = 0;
if ( iid == IID_IOleClientSite )
*ppvObject = (IOleClientSite*)this;
if ( iid == IID_IUnknown )
*ppvObject = this;
if ( iid == IID_IDispatch )
*ppvObject = (IDispatch*)this;
if ( _bExternalPlace == false)
{
if ( iid == IID_IOleInPlaceSite )
*ppvObject = (IOleInPlaceSite*)this;
if ( iid == IID_IOleInPlaceFrame )
*ppvObject = (IOleInPlaceFrame*)this;
if ( iid == IID_IOleInPlaceUIWindow )
*ppvObject = (IOleInPlaceUIWindow*)this;
}
if ( iid == DIID_DWebBrowserEvents2 )
*ppvObject = (DWebBrowserEvents2 *)this;
if ( iid == IID_IDocHostUIHandler)
*ppvObject = (IDocHostUIHandler*)this;
if ( *ppvObject )
{
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
下面先说GetWindow
//IOleInPlaceSite methods
STDMETHODIMP WebBrowser::GetWindow(HWND *p)
{
*p = GetHWND();//需要设置p为当前框架的窗口,否则Ole对象不知道框架
return S_OK;
}
再来说: CanInPlaceActivate(); Ole对象会询问容器,我能不能插进来(脸红中~)? 得到准许后才能温柔的插入,粗暴是不行的哦!
//IOleInPlaceSite methods
STDMETHODIMP WebBrowser::CanInPlaceActivate()//If this function return S_FALSE, AX cannot activate in place!
{
if ( _bInPlaced )//Does WebBrowser Control already in placed?
{
_bCalledCanInPlace = true;
return S_OK;
}
return S_FALSE;
}
WebBrowser::GetWindowContext(...) 获取窗口上下文(内容)
STDMETHODIMP WebBrowser::GetWindowContext(IOleInPlaceFrame** ppFrame,IOleInPlaceUIWindow **ppDoc,LPRECT r1,LPRECT r2,LPOLEINPLACEFRAMEINFO o)
{
//因为IOleInPlaceFrame接口已经被我们的WebBrowser实现
//所以直接设置为this
*ppFrame = (IOleInPlaceFrame*)this;
AddRef();
*ppDoc = NULL;
// r1, r2设置为框架的大小, 让WB充满整个窗口
::GetClientRect( GetHWND() ,&_rcWebWnd );
*r1 = _rcWebWnd;
*r2 = _rcWebWnd;
//我们没有这方面的要求,所以仅仅初始化.
o->cb = sizeof(OLEINPLACEFRAMEINFO);
o->fMDIApp = false;
o->hwndFrame = GetParent( GetHWND() );
o->haccel = 0;
o->cAccelEntries = 0;
return S_OK;
}
好了,下面是最后一个需要加料的函数了: IOleInPlaceFrame 接口的方法: GetBorder
注意这个接口正是上面说过的函数GetWindowContent 传递给OleObj对象的
//IOleInPlaceFrame methods|
STDMETHODIMP WebBrowser::GetBorder(LPRECT l)
{
::GetClientRect( GetHWND() ,&_rcWebWnd );
*l = _rcWebWnd;
return S_OK;
}
到现在为止,对于容器本身的实现已经全部完成了, 但是细心的读者会问到, GetHWND(void)还没有实现那!?
实际上,WebBrowser 作为一个基本类,不应当实现GetHWND(), 相反地, 他应当作为一个纯虚函数,要求其子类实现.
然后,我做了一个实验, 发现作为纯虚函数, 不能在类的构造函数中被调用, 或者间接调用. 而我们在WebBrowser::WebBrowser()中已经间接调用了GetHWND(),所以会有问题, 于是我们耍赖,把GetHWND(){return NULL;}
这样可以解决虚函数的问题.
四. 所有实现
WebBroser.h
class WebBrowser:
public IDispatch,
public IOleClientSite,
public IOleInPlaceSite,
public IOleInPlaceFrame,
public IDocHostUIHandler
{
public:
WebBrowser();
~WebBrowser(void);
public:
// IUnknown methods
virtual STDMETHODIMP QueryInterface(REFIID iid,void**ppvObject);
virtual STDMETHODIMP_(ULONG) AddRef();
virtual STDMETHODIMP_(ULONG) Release();
// IDispatch Methods
HRESULT _stdcall GetTypeInfoCount(unsigned int * pctinfo);
HRESULT _stdcall GetTypeInfo(unsigned int iTInfo,LCID lcid,ITypeInfo FAR* FAR* ppTInfo);
HRESULT _stdcall GetIDsOfNames(REFIID riid,OLECHAR FAR* FAR* rgszNames,unsigned int cNames,LCID lcid,DISPID FAR* rgDispId);
HRESULT _stdcall Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS FAR* pDispParams,VARIANT FAR* pVarResult,EXCEPINFO FAR* pExcepInfo,unsigned int FAR* puArgErr);
// IOleClientSite methods
virtual STDMETHODIMP SaveObject();
virtual STDMETHODIMP GetMoniker(DWORD dwA,DWORD dwW,IMoniker**pm);
virtual STDMETHODIMP GetContainer(IOleContainer**pc);
virtual STDMETHODIMP ShowObject();
virtual STDMETHODIMP OnShowWindow(BOOL f);
virtual STDMETHODIMP RequestNewObjectLayout();
// IOleInPlaceSite methods
virtual STDMETHODIMP GetWindow(HWND *p);
virtual STDMETHODIMP ContextSensitiveHelp(BOOL);
virtual STDMETHODIMP CanInPlaceActivate();
virtual STDMETHODIMP OnInPlaceActivate();
virtual STDMETHODIMP OnUIActivate();
virtual STDMETHODIMP GetWindowContext(IOleInPlaceFrame** ppFrame,IOleInPlaceUIWindow **ppDoc,LPRECT r1,LPRECT r2,LPOLEINPLACEFRAMEINFO o);
virtual STDMETHODIMP Scroll(SIZE s);
virtual STDMETHODIMP OnUIDeactivate(int);
virtual STDMETHODIMP OnInPlaceDeactivate();
virtual STDMETHODIMP DiscardUndoState();
virtual STDMETHODIMP DeactivateAndUndo();
virtual STDMETHODIMP OnPosRectChange(LPCRECT);
// IOleInPlaceFrame methods
virtual STDMETHODIMP GetBorder(LPRECT l);
virtual STDMETHODIMP RequestBorderSpace(LPCBORDERWIDTHS);
virtual STDMETHODIMP SetBorderSpace(LPCBORDERWIDTHS w);
virtual STDMETHODIMP SetActiveObject(IOleInPlaceActiveObject*pV,LPCOLESTR s);
virtual STDMETHODIMP InsertMenus(HMENU h,LPOLEMENUGROUPWIDTHS x);
virtual STDMETHODIMP SetMenu(HMENU h,HOLEMENU hO,HWND hw);
virtual STDMETHODIMP RemoveMenus(HMENU h);
virtual STDMETHODIMP SetStatusText(LPCOLESTR t);
virtual STDMETHODIMP EnableModeless(BOOL f);
virtual STDMETHODIMP TranslateAccelerator(LPMSG,WORD);
protected:
virtual HWND GetHWND(){return NULL;};//继承的类应该实现这个方法,告诉WebBrowser,到底用哪一个HWND放置WebBrowser
// 内部工具函数
private:
inline IOleObject* _GetOleObject(){return _pOleObj;};
inline IOleInPlaceObject* _GetInPlaceObject(){return _pInPlaceObj;};
//外部方法
public:
IWebBrowser2* GetWebBrowser2();
IHTMLDocument2* GetHTMLDocument2();
IHTMLDocument3* GetHTMLDocument3();
IHTMLWindow2* GetHTMLWindow2();
IHTMLEventObj* GetHTMLEventObject();
BOOL SetWebRect(LPRECT lprc);
BOOL OpenWebBrowser();
BOOL OpenURL(VARIANT* pVarUrl);
// 内部数据
protected:
long _refNum;
private:
RECT _rcWebWnd;
bool _bInPlaced;
bool _bExternalPlace;
bool _bCalledCanInPlace;
bool _bWebWndInited;
private:
//指针
IOleObject* _pOleObj;
IOleInPlaceObject* _pInPlaceObj;
IStorage* _pStorage;
IWebBrowser2* _pWB2;
IHTMLDocument2* _pHtmlDoc2;
IHTMLDocument3* _pHtmlDoc3;
IHTMLWindow2* _pHtmlWnd2;
IHTMLEventObj* _pHtmlEvent;
};
WebBrowser.cpp
注意: 里面有许多讨厌的东西, 是我加的饲料. 解释一下,我个人很喜欢这种错误处理方式.
读者可以简单的认为: (因为实际上我的实现不是那么简单,因为这个再说就有点复杂,所以简单化)
NULLTEST_SE( fn , wstr ) ; 如果 fn == 0 , 则显示 wstr, 并且跳转到RETURN
HRTEST_SE( fn, wstr); 如果 fn!=S_OK ,则显示wstr,并且跳转到RETURN
另外,考虑到WebBrowser以后总是作为另外一个类的父类,所以他完全不会因为计数器归零而自删除.
#include "WebBrowser.h"
/*
==================
| 构造和析构 |
==================
*/
WebBrowser::WebBrowser(void):
_refNum(0),
//_rcWebWnd(0),
_bInPlaced(false),
_bExternalPlace(false),
_bCalledCanInPlace(false),
_bWebWndInited(false),
_pOleObj(NULL),
_pInPlaceObj(NULL),
_pStorage(NULL),
_pWB2(NULL),
_pHtmlDoc2(NULL),
_pHtmlDoc3(NULL),
_pHtmlWnd2(NULL),
_pHtmlEvent(NULL)
{
::memset( (PVOID)&_rcWebWnd,0,sizeof(_rcWebWnd));
HRTEST_SE( OleInitialize(0),L"Ole初始化错误");
HRTEST_SE( StgCreateDocfile(0,STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_CREATE,0,&_pStorage),L"StgCreateDocfile错误");
HRTEST_SE( OleCreate(CLSID_WebBrowser,IID_IOleObject,OLERENDER_DRAW,0,this,_pStorage,(void**)&_pOleObj),L"Ole创建失败");
HRTEST_SE( _pOleObj->QueryInterface(IID_IOleInPlaceObject,(LPVOID*)&_pInPlaceObj),L"OleInPlaceObject创建失败");
RETURN:
return;
}
WebBrowser::~WebBrowser(void)
{
}
/*
==================
|IUnknown methods|
==================
*/
STDMETHODIMP WebBrowser::QueryInterface(REFIID iid,void**ppvObject)
{
*ppvObject = 0;
if ( iid == IID_IOleClientSite )
*ppvObject = (IOleClientSite*)this;
if ( iid == IID_IUnknown )
*ppvObject = this;
if ( iid == IID_IDispatch )
*ppvObject = (IDispatch*)this;
if ( _bExternalPlace == false)
{
if ( iid == IID_IOleInPlaceSite )
*ppvObject = (IOleInPlaceSite*)this;
if ( iid == IID_IOleInPlaceFrame )
*ppvObject = (IOleInPlaceFrame*)this;
if ( iid == IID_IOleInPlaceUIWindow )
*ppvObject = (IOleInPlaceUIWindow*)this;
}
/*
这里是一点走私货, 留在以后讲,如果有机会,你可以发现,原来如此简单.
if ( iid == DIID_DWebBrowserEvents2 )
*ppvObject = (DWebBrowserEvents2 *)this;
if ( iid == IID_IDocHostUIHandler)
*ppvObject = (IDocHostUIHandler*)this;
*/
if ( *ppvObject )
{
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) WebBrowser::AddRef()
{
return ::InterlockedIncrement( &_refNum );
}
STDMETHODIMP_(ULONG) WebBrowser::Release()
{
return ::InterlockedDecrement( &_refNum );
}
/*
=====================
| IDispatch Methods |
=====================
*/
HRESULT _stdcall WebBrowser::GetTypeInfoCount(
unsigned int * pctinfo)
{
return E_NOTIMPL;
}
HRESULT _stdcall WebBrowser::GetTypeInfo(
unsigned int iTInfo,
LCID lcid,
ITypeInfo FAR* FAR* ppTInfo)
{
return E_NOTIMPL;
}
HRESULT _stdcall WebBrowser::GetIDsOfNames(REFIID riid,
OLECHAR FAR* FAR* rgszNames,
unsigned int cNames,
LCID lcid,
DISPID FAR* rgDispId )
{
return E_NOTIMPL;
}
HRESULT _stdcall WebBrowser::Invoke(
DISPID dispIdMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pDispParams,
VARIANT* pVarResult,
EXCEPINFO* pExcepInfo,
unsigned int* puArgErr)
{
/*走私货,留在以后讲,是关于DWebBrowserEvents2让人激动的实现,而且简单.
// DWebBrowserEvents2
if( dispIdMember == DISPID_DOCUMENTCOMPLETE)
{
DocumentComplete(pDispParams->rgvarg[1].pdispVal,pDispParams->rgvarg[0].pvarVal);
return S_OK;
}
if( dispIdMember == DISPID_BEFORENAVIGATE2)
{
BeforeNavigate2( pDispParams->rgvarg[6].pdispVal,
pDispParams->rgvarg[5].pvarVal,
pDispParams->rgvarg[4].pvarVal,
pDispParams->rgvarg[3].pvarVal,
pDispParams->rgvarg[2].pvarVal,
pDispParams->rgvarg[1].pvarVal,
pDispParams->rgvarg[0].pboolVal);
return S_OK;
}
*/
return E_NOTIMPL;
}
/*
========================
|IOleClientSite methods|
========================
*/
STDMETHODIMP WebBrowser::SaveObject()
{
return S_OK;
}
STDMETHODIMP WebBrowser::GetMoniker(DWORD dwA,DWORD dwW,IMoniker**pm)
{
*pm = 0;
return E_NOTIMPL;
}
STDMETHODIMP WebBrowser::GetContainer(IOleContainer**pc)
{
*pc = 0;
return E_FAIL;
}
STDMETHODIMP WebBrowser::ShowObject()
{
return S_OK;
}
STDMETHODIMP WebBrowser::OnShowWindow(BOOL f)
{
return S_OK;
}
STDMETHODIMP WebBrowser::RequestNewObjectLayout()
{
return S_OK;
}
/*
=========================
|IOleInPlaceSite methods|
=========================
*/
STDMETHODIMP WebBrowser::GetWindow(HWND *p)
{
*p = GetHWND();
return S_OK;
}
STDMETHODIMP WebBrowser::ContextSensitiveHelp(BOOL)
{
return E_NOTIMPL;
}
STDMETHODIMP WebBrowser::CanInPlaceActivate()//If this function return S_FALSE, AX cannot activate in place!
{
if ( _bInPlaced )//Does WebBrowser Control already in placed?
{
_bCalledCanInPlace = true;
return S_OK;
}
return S_FALSE;
}
STDMETHODIMP WebBrowser::OnInPlaceActivate()
{
return S_OK;
}
STDMETHODIMP WebBrowser::OnUIActivate()
{
return S_OK;
}
STDMETHODIMP WebBrowser::GetWindowContext(IOleInPlaceFrame** ppFrame,IOleInPlaceUIWindow **ppDoc,LPRECT r1,LPRECT r2,LPOLEINPLACEFRAMEINFO o)
{
*ppFrame = (IOleInPlaceFrame*)this;
AddRef();
*ppDoc = NULL;
::GetClientRect( GetHWND() ,&_rcWebWnd );
*r1 = _rcWebWnd;
*r2 = _rcWebWnd;
o->cb = sizeof(OLEINPLACEFRAMEINFO);
o->fMDIApp = false;
o->hwndFrame = GetParent( GetHWND() );
o->haccel = 0;
o->cAccelEntries = 0;
return S_OK;
}
STDMETHODIMP WebBrowser::Scroll(SIZE s)
{
return E_NOTIMPL;
}
STDMETHODIMP WebBrowser::OnUIDeactivate(int)
{
return S_OK;
}
STDMETHODIMP WebBrowser::OnInPlaceDeactivate()
{
return S_OK;
}
STDMETHODIMP WebBrowser::DiscardUndoState()
{
return S_OK;
}
STDMETHODIMP WebBrowser::DeactivateAndUndo()
{
return S_OK;
}
STDMETHODIMP WebBrowser::OnPosRectChange(LPCRECT)
{
return S_OK;
}
/*
==========================
|IOleInPlaceFrame methods|
==========================
*/
STDMETHODIMP WebBrowser::GetBorder(LPRECT l)
{
::GetClientRect( GetHWND() ,&_rcWebWnd );
*l = _rcWebWnd;
return S_OK;
}
STDMETHODIMP WebBrowser::RequestBorderSpace(LPCBORDERWIDTHS b)
{
return S_OK;
}
STDMETHODIMP WebBrowser::SetBorderSpace(LPCBORDERWIDTHS b)
{
return S_OK;
}
STDMETHODIMP WebBrowser::SetActiveObject(IOleInPlaceActiveObject*pV,LPCOLESTR s)
{
return S_OK;
}
STDMETHODIMP WebBrowser::SetStatusText(LPCOLESTR t)
{
return E_NOTIMPL;
}
STDMETHODIMP WebBrowser::EnableModeless(BOOL f)
{
return E_NOTIMPL;
}
STDMETHODIMP WebBrowser::TranslateAccelerator(LPMSG,WORD)
{
return E_NOTIMPL;
}
HRESULT _stdcall WebBrowser::RemoveMenus(HMENU h)
{
return E_NOTIMPL;
}
HRESULT _stdcall WebBrowser::InsertMenus(HMENU h,LPOLEMENUGROUPWIDTHS x)
{
return E_NOTIMPL;
}
HRESULT _stdcall WebBrowser::SetMenu(HMENU h,HOLEMENU hO,HWND hw)
{
return E_NOTIMPL;
}
/*
====================
|DWebBrowserEvents2|
====================
*/
/* 走私货,以后再讲
void
WebBrowser::DocumentComplete( IDispatch *pDisp,VARIANT *URL)
{
//老天保佑,多好的函数啊.
return ;
}
void
WebBrowser::BeforeNavigate2( IDispatch *pDisp,VARIANT *&url,VARIANT *&Flags,VARIANT *&TargetFrameName,VARIANT *&PostData,VARIANT *&Headers,VARIANT_BOOL *&Cancel)
{
PCWSTR pcwApp = L"app:";
if( url->vt != VT_BSTR )
return;
if( 0 == _wcsnicmp( pcwApp, url->bstrVal,wcslen(pcwApp)) )
{
*Cancel = VARIANT_TRUE;
_OnHtmlCmd( url->bstrVal+wcslen(pcwApp) );
return;
}
*Cancel = VARIANT_FALSE;
}
*/
/*
=====================
| IDocHostUIHandler |
=====================
*/
/*
传说中的IDocHostUIHanler,同样留在以后讲
HRESULT WebBrowser:: ShowContextMenu(
DWORD dwID,
POINT *ppt,
IUnknown *pcmdtReserved,
IDispatch *pdispReserved){return E_NOTIMPL;}
HRESULT WebBrowser:: GetHostInfo(
DOCHOSTUIINFO *pInfo){return E_NOTIMPL;}
HRESULT WebBrowser:: ShowUI(
DWORD dwID,
IOleInPlaceActiveObject *pActiveObject,
IOleCommandTarget *pCommandTarget,
IOleInPlaceFrame *pFrame,
IOleInPlaceUIWindow *pDoc){return E_NOTIMPL;}
HRESULT WebBrowser:: HideUI( void){return E_NOTIMPL;}
HRESULT WebBrowser:: UpdateUI( void){return E_NOTIMPL;}
//HRESULT WebBrowser:: EnableModeless(
// BOOL fEnable){return E_NOTIMPL;}
HRESULT WebBrowser:: OnDocWindowActivate(
BOOL fActivate){return E_NOTIMPL;}
HRESULT WebBrowser:: OnFrameWindowActivate(
BOOL fActivate){return E_NOTIMPL;}
HRESULT WebBrowser:: ResizeBorder(
LPCRECT prcBorder,
IOleInPlaceUIWindow *pUIWindow,
BOOL fRameWindow){return E_NOTIMPL;}
HRESULT WebBrowser:: TranslateAccelerator(
LPMSG lpMsg,
const GUID *pguidCmdGroup,
DWORD nCmdID){return E_NOTIMPL;}
HRESULT WebBrowser:: GetOptionKeyPath(
LPOLESTR *pchKey,
DWORD dw){return E_NOTIMPL;}
HRESULT WebBrowser:: GetDropTarget(
IDropTarget *pDropTarget,
IDropTarget **ppDropTarget)
{
return E_NOTIMPL;//使用默认拖拽
//return S_OK;//自定义拖拽
}
HRESULT WebBrowser:: GetExternal( IDispatch **ppDispatch)
{
return E_NOTIMPL;
}
HRESULT WebBrowser:: TranslateUrl(
DWORD dwTranslate,
OLECHAR *pchURLIn,
OLECHAR **ppchURLOut){return E_NOTIMPL;}
HRESULT WebBrowser:: FilterDataObject(
IDataObject *pDO,
IDataObject **ppDORet){return E_NOTIMPL;}
*/
/*
===============
|Other Methods|
===============
*/
IWebBrowser2*
WebBrowser::GetWebBrowser2()
{
if( _pWB2 != NULL )
return _pWB2;
NULLTEST_SE( _pOleObj,L"Ole对象为空");
HRTEST_SE( _pOleObj->QueryInterface(IID_IWebBrowser2,(void**)&_pWB2),L"QueryInterface IID_IWebBrowser2 失败");
return _pWB2;
RETURN:
return NULL;
}
IHTMLDocument2*
WebBrowser::GetHTMLDocument2()
{
if( _pHtmlDoc2 != NULL )
return _pHtmlDoc2;
IWebBrowser2* pWB2 = NULL;
NULLTEST(pWB2 = GetWebBrowser2());//GetWebBrowser2已经将错误原因交给LastError.
IDispatch* pDp = NULL;
HRTEST_SE(pWB2->get_Document(&pDp),L"DWebBrowser2::get_Document 错误");
HRTEST_SE(pDp->QueryInterface(IID_IHTMLDocument2,(void**)&_pHtmlDoc2),L"QueryInterface IID_IHTMLDocument2 失败");
return _pHtmlDoc2;
RETURN:
return NULL;
}
IHTMLDocument3*
WebBrowser::GetHTMLDocument3()
{
if( _pHtmlDoc3 != NULL )
return _pHtmlDoc3;
IWebBrowser2* pWB2 = NULL;
NULLTEST(pWB2 = GetWebBrowser2());//GetWebBrowser2已经将错误原因交给LastError.
IDispatch* pDp = NULL;
HRTEST_SE(pWB2->get_Document(&pDp),L"DWebBrowser2::get_Document 错误");
HRTEST_SE(pDp->QueryInterface(IID_IHTMLDocument3,(void**)&_pHtmlDoc3),L"QueryInterface IID_IHTMLDocument3 失败");
return _pHtmlDoc3;
RETURN:
return NULL;
}
IHTMLWindow2*
WebBrowser::GetHTMLWindow2()
{
if( _pHtmlWnd2 != NULL)
return _pHtmlWnd2;
IHTMLDocument2* pHD2 = GetHTMLDocument2();
NULLTEST( pHD2 );
HRTEST_SE( pHD2->get_parentWindow(&_pHtmlWnd2),L"IHTMLWindow2::get_parentWindow 错误" );
return _pHtmlWnd2;
RETURN:
return NULL;
}
IHTMLEventObj*
WebBrowser::GetHTMLEventObject()
{
if( _pHtmlEvent != NULL )
return _pHtmlEvent;
IHTMLWindow2* pHW2;
NULLTEST( pHW2 = GetHTMLWindow2() );
HRTEST_SE( pHW2->get_event(&_pHtmlEvent),L"IHTMLWindow2::get_event 错误");
return _pHtmlEvent;
RETURN:
return NULL;
}
BOOL
WebBrowser::SetWebRect(LPRECT lprc)
{
BOOL bRet = FALSE;
if( false == _bInPlaced )//尚未OpenWebBrowser操作,直接写入_rcWebWnd
{
_rcWebWnd = *lprc;
}
else//已经打开WebBrowser,通过 IOleInPlaceObject::SetObjectRects 调整大小
{
SIZEL size;
size.cx = RECTWIDTH(*lprc);
size.cy = RECTHEIGHT(*lprc);
IOleObject* pOleObj;
NULLTEST( pOleObj= _GetOleObject());
HRTEST_E( pOleObj->SetExtent( 1,&size ),L"SetExtent 错误");
IOleInPlaceObject* pInPlace;
NULLTEST( pInPlace = _GetInPlaceObject());
HRTEST_E( pInPlace->SetObjectRects(lprc,lprc),L"SetObjectRects 错误");
_rcWebWnd = *lprc;
}
bRet = TRUE;
RETURN:
return bRet;
}
BOOL
WebBrowser::OpenWebBrowser()
{
BOOL bRet = FALSE;
NULLTEST_E( _GetOleObject(),L"ActiveX对象为空" );//对于本身的实现函数,其自身承担错误录入工作
if( (RECTWIDTH(_rcWebWnd) && RECTHEIGHT(_rcWebWnd)) == 0 )
::GetClientRect( GetHWND() ,&_rcWebWnd);//设置WebBrowser的大小为窗口的客户区大小.
if( _bInPlaced == false )// Activate In Place
{
_bInPlaced = true;//_bInPlaced must be set as true, before INPLACEACTIVATE, otherwise, once DoVerb, it would return error;
_bExternalPlace = 0;//lParam;
HRTEST_E( _GetOleObject()->DoVerb(OLEIVERB_INPLACEACTIVATE,0,this,0, GetHWND() ,&_rcWebWnd),L"关于INPLACE的DoVerb错误");
_bInPlaced = true;
//* 挂接DWebBrwoser2Event
IConnectionPointContainer* pCPC = NULL;
IConnectionPoint* pCP = NULL;
HRTEST_E( GetWebBrowser2()->QueryInterface(IID_IConnectionPointContainer,(void**)&pCPC),L"枚举IConnectionPointContainer接口失败");
HRTEST_E( pCPC->FindConnectionPoint( DIID_DWebBrowserEvents2,&pCP),L"FindConnectionPoint失败");
DWORD dwCookie = 0;
HRTEST_E( pCP->Advise( (IUnknown*)(void*)this,&dwCookie),L"IConnectionPoint::Advise失败");
}
bRet = TRUE;
RETURN:
return bRet;
}
BOOL
WebBrowser::OpenURL(VARIANT* pVarUrl)
{
BOOL bRet = FALSE;
HRTEST_E( GetWebBrowser2()->Navigate2( pVarUrl,0,0,0,0),L"GetWebBrowser2 失败");
bRet = TRUE;
RETURN:
return bRet;
}
五.例子
class WebBrowserWindow :
public WebBrowser
{
public:
WebBrowserWindow(void);
~WebBrowserWindow(void);
public:
virtual HWND GetHWND(){ return YourWindow};
private:
LRESULT OnCreate( WPARAM wParam,LPARAM lParam);
//您可以在OnCreate中调用OpenWebBrowser 然后调用 OpenURL
};
六.下期预告,如果有的话
如果有人感兴趣的话, 以后会说道如何扩展WebBrowser, 让他包罗万象, 你会发现很简单.
如果有问题,欢迎提问. 谢谢.
一.由来
我还记还得当自认为学习完了C++语法后, 兴致勃勃的打开MFC向导,开始所谓"MFC高级自动化编程"时, 我不由喊道: 这他妈的都是些什么乱七八糟的东西啊.诚然,MFC为了方便愚蠢的程序员,的确是让人瞬间高级编程, 可是愚蠢的我们还是会用CString strTest;写出(PWSTR)strTest.GetBuffer() 这种自作聪明的用法,结果就是整个程序好像发了疯.
终于有一天,我大喊一声: 滚蛋吧,MFC!
太阳当空照,上帝对 我笑: 你Y去写个有WebBrowser控件的窗口吧
二.结构
MFC的HTMLDialog 是一个魔盒, 他容易上手,可是却很难深入,究其缘由, 私以为无外乎MFC包装的太好, 不识庐山真面目,只缘身在此山中. 总是以为作为C类型的程序员,喜欢深入原理是职业特性.
于是首先看到有强人写的纯C实现WebBrowser控件,他在这里: http://www.codeproject.com/KB/COM/cwebpage.aspx 这位同志很好的展示了在没有C++的支持下,支持ActiveX是多么痛苦的一件事情...........汗.
在下觉得,撇开C++的类的特性,虚拟特性,重载特性,而去用纯C实现,实在有点过犹不及,当然这种练习修炼内功很好,但是实际应用上显得麻烦.毕竟,ActiveX很好的使用了C++的特性,而不是C的特性.
接着,开始使用纯SDK编写容器. 读者可以搜索csdn中关于SDK实现WebBrowser容器的帖子, 许多人说那是非常繁琐复杂.所谓人云亦云,各位看官不妨跟着我一试,且看到底何如?
WebBrowser容器的实现需要许多接口, 也许正是这吓退了许多人, 实际情况是,许多接口的方法没几个需要实现,大部分只需要直接返回E_NOTIMPL和S_OK, E_FAIL.
让我们命名我们编写的容器叫 WebBrowser (可能名字有点糊涂,但是因为源代码中就是这个名字,所以就不改了,大家只需要注意,这个是一个WebBrowser控件的容器 ), 在实现这个容器后, 我们把他作为一个窗口类的父类,这样 就能实现WebBrowser的页面窗口.
首先一个WB( WebBrowser) 容器需要以下接口:
public IDispatch
public IOleClientSite
public IOleInPlaceSite
public IOleInPlaceFrame
每个接口需要实现的方法分别是:
// IUnknown methods
virtual STDMETHODIMP QueryInterface(REFIID iid,void**ppvObject);
virtual STDMETHODIMP_(ULONG) AddRef();
virtual STDMETHODIMP_(ULONG) Release();
// IDispatch Methods
HRESULT _stdcall GetTypeInfoCount(unsigned int * pctinfo);
HRESULT _stdcall GetTypeInfo(unsigned int iTInfo,LCID lcid,ITypeInfo FAR* FAR* ppTInfo);
HRESULT _stdcall GetIDsOfNames(REFIID riid,OLECHAR FAR* FAR* rgszNames,unsigned int cNames,LCID lcid,DISPID FAR* rgDispId);
HRESULT _stdcall Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS FAR* pDispParams,VARIANT FAR* pVarResult,EXCEPINFO FAR* pExcepInfo,unsigned int FAR* puArgErr);
// IOleClientSite methods
virtual STDMETHODIMP SaveObject();
virtual STDMETHODIMP GetMoniker(DWORD dwA,DWORD dwW,IMoniker**pm);
virtual STDMETHODIMP GetContainer(IOleContainer**pc);
virtual STDMETHODIMP ShowObject();
virtual STDMETHODIMP OnShowWindow(BOOL f);
virtual STDMETHODIMP RequestNewObjectLayout();
// IOleInPlaceSite methods
virtual STDMETHODIMP GetWindow(HWND *p);
virtual STDMETHODIMP ContextSensitiveHelp(BOOL);
virtual STDMETHODIMP CanInPlaceActivate();
virtual STDMETHODIMP OnInPlaceActivate();
virtual STDMETHODIMP OnUIActivate();
virtual STDMETHODIMP GetWindowContext(IOleInPlaceFrame** ppFrame,IOleInPlaceUIWindow **ppDoc,LPRECT r1,LPRECT r2,LPOLEINPLACEFRAMEINFO o);
virtual STDMETHODIMP Scroll(SIZE s);
virtual STDMETHODIMP OnUIDeactivate(int);
virtual STDMETHODIMP OnInPlaceDeactivate();
virtual STDMETHODIMP DiscardUndoState();
virtual STDMETHODIMP DeactivateAndUndo();
virtual STDMETHODIMP OnPosRectChange(LPCRECT);
// IOleInPlaceFrame methods
virtual STDMETHODIMP GetBorder(LPRECT l);
virtual STDMETHODIMP RequestBorderSpace(LPCBORDERWIDTHS);
virtual STDMETHODIMP SetBorderSpace(LPCBORDERWIDTHS w);
virtual STDMETHODIMP SetActiveObject(IOleInPlaceActiveObject*pV,LPCOLESTR s);
virtual STDMETHODIMP InsertMenus(HMENU h,LPOLEMENUGROUPWIDTHS x);
virtual STDMETHODIMP SetMenu(HMENU h,HOLEMENU hO,HWND hw);
virtual STDMETHODIMP RemoveMenus(HMENU h);
virtual STDMETHODIMP SetStatusText(LPCOLESTR t);
virtual STDMETHODIMP EnableModeless(BOOL f);
virtual STDMETHODIMP TranslateAccelerator(LPMSG,WORD);
这里插几句题外话, 我讨厌看文章, 因为萝莉罗嗦也没看到那个爆炸点, 就是你看到他,一下子思路有了头绪, 不再是一头雾水了.可惜,我看到的大部分文章都不是这样, 为了避免这个问题, 我在这里放个屁, 嗯,也算是一个小小的爆破吧.
好,上面已经罗列了需要的接口和接口的方法.
要想实现WB的容器,你必须有这段代码:
WebBrowser::WebBrowser()
{
//初始化OLE
OleInitialize(0);
//创建IStorage对象,其中_pStorage是WebBrowser的私有变量
StgCreateDocfile(0,STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_CREATE,0,&_pStorage);
//创建IOleObject对象,我们看到,短短的一条语句,已经几乎创建完成了WebBrowser!
//this在这里时表示当前类(即WebBrowser)作为容器,承载_pOleObj这个控件
//这里就是放屁现场了,大家好好看看哈.
OleCreate( CLSID_WebBrowser,IID_IOleObject,OLERENDER_DRAW,
0 , this, _pStorage, (void**)&_pOleObj );
//获得IOleInPlaceObject对象
_pOleObj->QueryInterface(IID_IOleInPlaceObject,(LPVOID*)&_pInPlaceObj);
}
上面一段代码已经创建了wb的Ole对象, 接下来,你只需要打开WB,然后打开网页.我们用两个函数实现,他们是OpenWebBrowser() 和 OpenURL( VARIANT* pVarUrl )
OpenWebBrowser:
BOOL
WebBrowser::OpenWebBrowser()
{
BOOL bRet = FALSE;
// RECTWIDTH 和 RECTHEIGHT 是计算RECT长宽的宏函数
// _rcWebWnd 是WebBrowser的私有RECT成员,就是GetHWND()的大小.
// 下面的语句就是关联两者
// GetHWND()获取容器的框架窗口句柄,就是我们创建的窗口,关于这个函数,下面会单独讲
if( (RECTWIDTH(_rcWebWnd) && RECTHEIGHT(_rcWebWnd)) == 0 )
::GetClientRect( GetHWND() ,&_rcWebWnd);//设置WebBrowser的大小为窗口的客户区大小.
// _bInPlaced 是WebBrowser的一个私有BOOL成员,初始为false
// 一旦执行了OLEIVERB_INPLACEACTIVATE (就是下面的操作)后,立即设置为true,防止控件被多次放置在容器中
// _GetOleObject()是WebBrowser的一个保护函数成员,他只是简单的返回_pOleObj
// _pOleObj就是WB控件本身. 考虑到WebBrowser是一个基本类,以后必然被其他类继承,所以使用_GetOleObject()来返回_pOleObject,来实现防止_pOleObject本身不被修改的意外.
if( _bInPlaced == false )// Activate In Place
{
_bInPlaced = true;//_bInPlaced must be set as true, before INPLACEACTIVATE, otherwise, once DoVerb, it would return error;
_bExternalPlace = 0;//lParam;
_GetOleObject()->DoVerb(OLEIVERB_INPLACEACTIVATE,0,this,0, GetHWND(),&_rcWebWnd);
_bInPlaced = true;
}
bRet = TRUE;
RETURN:
return bRet;
}
OpenURL(...)
BOOL
WebBrowser::OpenURL(VARIANT* pVarUrl)
{
BOOL bRet = FALSE;
//GetWebBrowser2 返回IWebBrowser2
//他的实现是这样的:
//if( _pWB2 != NULL )//_pWB2是WebBrowser的私有IWebBrowser2指针
// return _pWB2;//如果_pWB2已经不是NULL,即已经获取过内容,则直接返回
////否则使用WB控件对象枚举IWebBrowser2指针
//_pOleObj->QueryInterface(IID_IWebBrowser2,(void**)&_pWB2);
//return _pWB2;
GetWebBrowser2()->Navigate2( pVarUrl,0,0,0,0);//打开网页
bRet = TRUE;
RETURN:
return bRet;
}
三.细节
前面说过,那几个接口的方法,大部分都是直接返回E_NOTIMPL和S_OK, E_FAIL.,我说大部分,说明必然有些函数需要做一些事情,而且其作用很不小, 请看官别马虎了这段.
:)
IDispatch 接口中的 IUnknown 的实现实在是不必多言, 稍微说一下接口枚举:
STDMETHODIMP WebBrowser::QueryInterface(REFIID iid,void**ppvObject)
{
*ppvObject = 0;
if ( iid == IID_IOleClientSite )
*ppvObject = (IOleClientSite*)this;
if ( iid == IID_IUnknown )
*ppvObject = this;
if ( iid == IID_IDispatch )
*ppvObject = (IDispatch*)this;
if ( _bExternalPlace == false)
{
if ( iid == IID_IOleInPlaceSite )
*ppvObject = (IOleInPlaceSite*)this;
if ( iid == IID_IOleInPlaceFrame )
*ppvObject = (IOleInPlaceFrame*)this;
if ( iid == IID_IOleInPlaceUIWindow )
*ppvObject = (IOleInPlaceUIWindow*)this;
}
if ( iid == DIID_DWebBrowserEvents2 )
*ppvObject = (DWebBrowserEvents2 *)this;
if ( iid == IID_IDocHostUIHandler)
*ppvObject = (IDocHostUIHandler*)this;
if ( *ppvObject )
{
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
下面先说GetWindow
//IOleInPlaceSite methods
STDMETHODIMP WebBrowser::GetWindow(HWND *p)
{
*p = GetHWND();//需要设置p为当前框架的窗口,否则Ole对象不知道框架
return S_OK;
}
再来说: CanInPlaceActivate(); Ole对象会询问容器,我能不能插进来(脸红中~)? 得到准许后才能温柔的插入,粗暴是不行的哦!
//IOleInPlaceSite methods
STDMETHODIMP WebBrowser::CanInPlaceActivate()//If this function return S_FALSE, AX cannot activate in place!
{
if ( _bInPlaced )//Does WebBrowser Control already in placed?
{
_bCalledCanInPlace = true;
return S_OK;
}
return S_FALSE;
}
WebBrowser::GetWindowContext(...) 获取窗口上下文(内容)
STDMETHODIMP WebBrowser::GetWindowContext(IOleInPlaceFrame** ppFrame,IOleInPlaceUIWindow **ppDoc,LPRECT r1,LPRECT r2,LPOLEINPLACEFRAMEINFO o)
{
//因为IOleInPlaceFrame接口已经被我们的WebBrowser实现
//所以直接设置为this
*ppFrame = (IOleInPlaceFrame*)this;
AddRef();
*ppDoc = NULL;
// r1, r2设置为框架的大小, 让WB充满整个窗口
::GetClientRect( GetHWND() ,&_rcWebWnd );
*r1 = _rcWebWnd;
*r2 = _rcWebWnd;
//我们没有这方面的要求,所以仅仅初始化.
o->cb = sizeof(OLEINPLACEFRAMEINFO);
o->fMDIApp = false;
o->hwndFrame = GetParent( GetHWND() );
o->haccel = 0;
o->cAccelEntries = 0;
return S_OK;
}
好了,下面是最后一个需要加料的函数了: IOleInPlaceFrame 接口的方法: GetBorder
注意这个接口正是上面说过的函数GetWindowContent 传递给OleObj对象的
//IOleInPlaceFrame methods|
STDMETHODIMP WebBrowser::GetBorder(LPRECT l)
{
::GetClientRect( GetHWND() ,&_rcWebWnd );
*l = _rcWebWnd;
return S_OK;
}
到现在为止,对于容器本身的实现已经全部完成了, 但是细心的读者会问到, GetHWND(void)还没有实现那!?
实际上,WebBrowser 作为一个基本类,不应当实现GetHWND(), 相反地, 他应当作为一个纯虚函数,要求其子类实现.
然后,我做了一个实验, 发现作为纯虚函数, 不能在类的构造函数中被调用, 或者间接调用. 而我们在WebBrowser::WebBrowser()中已经间接调用了GetHWND(),所以会有问题, 于是我们耍赖,把GetHWND(){return NULL;}
这样可以解决虚函数的问题.
四. 所有实现
WebBroser.h
class WebBrowser:
public IDispatch,
public IOleClientSite,
public IOleInPlaceSite,
public IOleInPlaceFrame,
public IDocHostUIHandler
{
public:
WebBrowser();
~WebBrowser(void);
public:
// IUnknown methods
virtual STDMETHODIMP QueryInterface(REFIID iid,void**ppvObject);
virtual STDMETHODIMP_(ULONG) AddRef();
virtual STDMETHODIMP_(ULONG) Release();
// IDispatch Methods
HRESULT _stdcall GetTypeInfoCount(unsigned int * pctinfo);
HRESULT _stdcall GetTypeInfo(unsigned int iTInfo,LCID lcid,ITypeInfo FAR* FAR* ppTInfo);
HRESULT _stdcall GetIDsOfNames(REFIID riid,OLECHAR FAR* FAR* rgszNames,unsigned int cNames,LCID lcid,DISPID FAR* rgDispId);
HRESULT _stdcall Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS FAR* pDispParams,VARIANT FAR* pVarResult,EXCEPINFO FAR* pExcepInfo,unsigned int FAR* puArgErr);
// IOleClientSite methods
virtual STDMETHODIMP SaveObject();
virtual STDMETHODIMP GetMoniker(DWORD dwA,DWORD dwW,IMoniker**pm);
virtual STDMETHODIMP GetContainer(IOleContainer**pc);
virtual STDMETHODIMP ShowObject();
virtual STDMETHODIMP OnShowWindow(BOOL f);
virtual STDMETHODIMP RequestNewObjectLayout();
// IOleInPlaceSite methods
virtual STDMETHODIMP GetWindow(HWND *p);
virtual STDMETHODIMP ContextSensitiveHelp(BOOL);
virtual STDMETHODIMP CanInPlaceActivate();
virtual STDMETHODIMP OnInPlaceActivate();
virtual STDMETHODIMP OnUIActivate();
virtual STDMETHODIMP GetWindowContext(IOleInPlaceFrame** ppFrame,IOleInPlaceUIWindow **ppDoc,LPRECT r1,LPRECT r2,LPOLEINPLACEFRAMEINFO o);
virtual STDMETHODIMP Scroll(SIZE s);
virtual STDMETHODIMP OnUIDeactivate(int);
virtual STDMETHODIMP OnInPlaceDeactivate();
virtual STDMETHODIMP DiscardUndoState();
virtual STDMETHODIMP DeactivateAndUndo();
virtual STDMETHODIMP OnPosRectChange(LPCRECT);
// IOleInPlaceFrame methods
virtual STDMETHODIMP GetBorder(LPRECT l);
virtual STDMETHODIMP RequestBorderSpace(LPCBORDERWIDTHS);
virtual STDMETHODIMP SetBorderSpace(LPCBORDERWIDTHS w);
virtual STDMETHODIMP SetActiveObject(IOleInPlaceActiveObject*pV,LPCOLESTR s);
virtual STDMETHODIMP InsertMenus(HMENU h,LPOLEMENUGROUPWIDTHS x);
virtual STDMETHODIMP SetMenu(HMENU h,HOLEMENU hO,HWND hw);
virtual STDMETHODIMP RemoveMenus(HMENU h);
virtual STDMETHODIMP SetStatusText(LPCOLESTR t);
virtual STDMETHODIMP EnableModeless(BOOL f);
virtual STDMETHODIMP TranslateAccelerator(LPMSG,WORD);
protected:
virtual HWND GetHWND(){return NULL;};//继承的类应该实现这个方法,告诉WebBrowser,到底用哪一个HWND放置WebBrowser
// 内部工具函数
private:
inline IOleObject* _GetOleObject(){return _pOleObj;};
inline IOleInPlaceObject* _GetInPlaceObject(){return _pInPlaceObj;};
//外部方法
public:
IWebBrowser2* GetWebBrowser2();
IHTMLDocument2* GetHTMLDocument2();
IHTMLDocument3* GetHTMLDocument3();
IHTMLWindow2* GetHTMLWindow2();
IHTMLEventObj* GetHTMLEventObject();
BOOL SetWebRect(LPRECT lprc);
BOOL OpenWebBrowser();
BOOL OpenURL(VARIANT* pVarUrl);
// 内部数据
protected:
long _refNum;
private:
RECT _rcWebWnd;
bool _bInPlaced;
bool _bExternalPlace;
bool _bCalledCanInPlace;
bool _bWebWndInited;
private:
//指针
IOleObject* _pOleObj;
IOleInPlaceObject* _pInPlaceObj;
IStorage* _pStorage;
IWebBrowser2* _pWB2;
IHTMLDocument2* _pHtmlDoc2;
IHTMLDocument3* _pHtmlDoc3;
IHTMLWindow2* _pHtmlWnd2;
IHTMLEventObj* _pHtmlEvent;
};
WebBrowser.cpp
注意: 里面有许多讨厌的东西, 是我加的饲料. 解释一下,我个人很喜欢这种错误处理方式.
读者可以简单的认为: (因为实际上我的实现不是那么简单,因为这个再说就有点复杂,所以简单化)
NULLTEST_SE( fn , wstr ) ; 如果 fn == 0 , 则显示 wstr, 并且跳转到RETURN
HRTEST_SE( fn, wstr); 如果 fn!=S_OK ,则显示wstr,并且跳转到RETURN
另外,考虑到WebBrowser以后总是作为另外一个类的父类,所以他完全不会因为计数器归零而自删除.
#include "WebBrowser.h"
/*
==================
| 构造和析构 |
==================
*/
WebBrowser::WebBrowser(void):
_refNum(0),
//_rcWebWnd(0),
_bInPlaced(false),
_bExternalPlace(false),
_bCalledCanInPlace(false),
_bWebWndInited(false),
_pOleObj(NULL),
_pInPlaceObj(NULL),
_pStorage(NULL),
_pWB2(NULL),
_pHtmlDoc2(NULL),
_pHtmlDoc3(NULL),
_pHtmlWnd2(NULL),
_pHtmlEvent(NULL)
{
::memset( (PVOID)&_rcWebWnd,0,sizeof(_rcWebWnd));
HRTEST_SE( OleInitialize(0),L"Ole初始化错误");
HRTEST_SE( StgCreateDocfile(0,STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_CREATE,0,&_pStorage),L"StgCreateDocfile错误");
HRTEST_SE( OleCreate(CLSID_WebBrowser,IID_IOleObject,OLERENDER_DRAW,0,this,_pStorage,(void**)&_pOleObj),L"Ole创建失败");
HRTEST_SE( _pOleObj->QueryInterface(IID_IOleInPlaceObject,(LPVOID*)&_pInPlaceObj),L"OleInPlaceObject创建失败");
RETURN:
return;
}
WebBrowser::~WebBrowser(void)
{
}
/*
==================
|IUnknown methods|
==================
*/
STDMETHODIMP WebBrowser::QueryInterface(REFIID iid,void**ppvObject)
{
*ppvObject = 0;
if ( iid == IID_IOleClientSite )
*ppvObject = (IOleClientSite*)this;
if ( iid == IID_IUnknown )
*ppvObject = this;
if ( iid == IID_IDispatch )
*ppvObject = (IDispatch*)this;
if ( _bExternalPlace == false)
{
if ( iid == IID_IOleInPlaceSite )
*ppvObject = (IOleInPlaceSite*)this;
if ( iid == IID_IOleInPlaceFrame )
*ppvObject = (IOleInPlaceFrame*)this;
if ( iid == IID_IOleInPlaceUIWindow )
*ppvObject = (IOleInPlaceUIWindow*)this;
}
/*
这里是一点走私货, 留在以后讲,如果有机会,你可以发现,原来如此简单.
if ( iid == DIID_DWebBrowserEvents2 )
*ppvObject = (DWebBrowserEvents2 *)this;
if ( iid == IID_IDocHostUIHandler)
*ppvObject = (IDocHostUIHandler*)this;
*/
if ( *ppvObject )
{
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) WebBrowser::AddRef()
{
return ::InterlockedIncrement( &_refNum );
}
STDMETHODIMP_(ULONG) WebBrowser::Release()
{
return ::InterlockedDecrement( &_refNum );
}
/*
=====================
| IDispatch Methods |
=====================
*/
HRESULT _stdcall WebBrowser::GetTypeInfoCount(
unsigned int * pctinfo)
{
return E_NOTIMPL;
}
HRESULT _stdcall WebBrowser::GetTypeInfo(
unsigned int iTInfo,
LCID lcid,
ITypeInfo FAR* FAR* ppTInfo)
{
return E_NOTIMPL;
}
HRESULT _stdcall WebBrowser::GetIDsOfNames(REFIID riid,
OLECHAR FAR* FAR* rgszNames,
unsigned int cNames,
LCID lcid,
DISPID FAR* rgDispId )
{
return E_NOTIMPL;
}
HRESULT _stdcall WebBrowser::Invoke(
DISPID dispIdMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pDispParams,
VARIANT* pVarResult,
EXCEPINFO* pExcepInfo,
unsigned int* puArgErr)
{
/*走私货,留在以后讲,是关于DWebBrowserEvents2让人激动的实现,而且简单.
// DWebBrowserEvents2
if( dispIdMember == DISPID_DOCUMENTCOMPLETE)
{
DocumentComplete(pDispParams->rgvarg[1].pdispVal,pDispParams->rgvarg[0].pvarVal);
return S_OK;
}
if( dispIdMember == DISPID_BEFORENAVIGATE2)
{
BeforeNavigate2( pDispParams->rgvarg[6].pdispVal,
pDispParams->rgvarg[5].pvarVal,
pDispParams->rgvarg[4].pvarVal,
pDispParams->rgvarg[3].pvarVal,
pDispParams->rgvarg[2].pvarVal,
pDispParams->rgvarg[1].pvarVal,
pDispParams->rgvarg[0].pboolVal);
return S_OK;
}
*/
return E_NOTIMPL;
}
/*
========================
|IOleClientSite methods|
========================
*/
STDMETHODIMP WebBrowser::SaveObject()
{
return S_OK;
}
STDMETHODIMP WebBrowser::GetMoniker(DWORD dwA,DWORD dwW,IMoniker**pm)
{
*pm = 0;
return E_NOTIMPL;
}
STDMETHODIMP WebBrowser::GetContainer(IOleContainer**pc)
{
*pc = 0;
return E_FAIL;
}
STDMETHODIMP WebBrowser::ShowObject()
{
return S_OK;
}
STDMETHODIMP WebBrowser::OnShowWindow(BOOL f)
{
return S_OK;
}
STDMETHODIMP WebBrowser::RequestNewObjectLayout()
{
return S_OK;
}
/*
=========================
|IOleInPlaceSite methods|
=========================
*/
STDMETHODIMP WebBrowser::GetWindow(HWND *p)
{
*p = GetHWND();
return S_OK;
}
STDMETHODIMP WebBrowser::ContextSensitiveHelp(BOOL)
{
return E_NOTIMPL;
}
STDMETHODIMP WebBrowser::CanInPlaceActivate()//If this function return S_FALSE, AX cannot activate in place!
{
if ( _bInPlaced )//Does WebBrowser Control already in placed?
{
_bCalledCanInPlace = true;
return S_OK;
}
return S_FALSE;
}
STDMETHODIMP WebBrowser::OnInPlaceActivate()
{
return S_OK;
}
STDMETHODIMP WebBrowser::OnUIActivate()
{
return S_OK;
}
STDMETHODIMP WebBrowser::GetWindowContext(IOleInPlaceFrame** ppFrame,IOleInPlaceUIWindow **ppDoc,LPRECT r1,LPRECT r2,LPOLEINPLACEFRAMEINFO o)
{
*ppFrame = (IOleInPlaceFrame*)this;
AddRef();
*ppDoc = NULL;
::GetClientRect( GetHWND() ,&_rcWebWnd );
*r1 = _rcWebWnd;
*r2 = _rcWebWnd;
o->cb = sizeof(OLEINPLACEFRAMEINFO);
o->fMDIApp = false;
o->hwndFrame = GetParent( GetHWND() );
o->haccel = 0;
o->cAccelEntries = 0;
return S_OK;
}
STDMETHODIMP WebBrowser::Scroll(SIZE s)
{
return E_NOTIMPL;
}
STDMETHODIMP WebBrowser::OnUIDeactivate(int)
{
return S_OK;
}
STDMETHODIMP WebBrowser::OnInPlaceDeactivate()
{
return S_OK;
}
STDMETHODIMP WebBrowser::DiscardUndoState()
{
return S_OK;
}
STDMETHODIMP WebBrowser::DeactivateAndUndo()
{
return S_OK;
}
STDMETHODIMP WebBrowser::OnPosRectChange(LPCRECT)
{
return S_OK;
}
/*
==========================
|IOleInPlaceFrame methods|
==========================
*/
STDMETHODIMP WebBrowser::GetBorder(LPRECT l)
{
::GetClientRect( GetHWND() ,&_rcWebWnd );
*l = _rcWebWnd;
return S_OK;
}
STDMETHODIMP WebBrowser::RequestBorderSpace(LPCBORDERWIDTHS b)
{
return S_OK;
}
STDMETHODIMP WebBrowser::SetBorderSpace(LPCBORDERWIDTHS b)
{
return S_OK;
}
STDMETHODIMP WebBrowser::SetActiveObject(IOleInPlaceActiveObject*pV,LPCOLESTR s)
{
return S_OK;
}
STDMETHODIMP WebBrowser::SetStatusText(LPCOLESTR t)
{
return E_NOTIMPL;
}
STDMETHODIMP WebBrowser::EnableModeless(BOOL f)
{
return E_NOTIMPL;
}
STDMETHODIMP WebBrowser::TranslateAccelerator(LPMSG,WORD)
{
return E_NOTIMPL;
}
HRESULT _stdcall WebBrowser::RemoveMenus(HMENU h)
{
return E_NOTIMPL;
}
HRESULT _stdcall WebBrowser::InsertMenus(HMENU h,LPOLEMENUGROUPWIDTHS x)
{
return E_NOTIMPL;
}
HRESULT _stdcall WebBrowser::SetMenu(HMENU h,HOLEMENU hO,HWND hw)
{
return E_NOTIMPL;
}
/*
====================
|DWebBrowserEvents2|
====================
*/
/* 走私货,以后再讲
void
WebBrowser::DocumentComplete( IDispatch *pDisp,VARIANT *URL)
{
//老天保佑,多好的函数啊.
return ;
}
void
WebBrowser::BeforeNavigate2( IDispatch *pDisp,VARIANT *&url,VARIANT *&Flags,VARIANT *&TargetFrameName,VARIANT *&PostData,VARIANT *&Headers,VARIANT_BOOL *&Cancel)
{
PCWSTR pcwApp = L"app:";
if( url->vt != VT_BSTR )
return;
if( 0 == _wcsnicmp( pcwApp, url->bstrVal,wcslen(pcwApp)) )
{
*Cancel = VARIANT_TRUE;
_OnHtmlCmd( url->bstrVal+wcslen(pcwApp) );
return;
}
*Cancel = VARIANT_FALSE;
}
*/
/*
=====================
| IDocHostUIHandler |
=====================
*/
/*
传说中的IDocHostUIHanler,同样留在以后讲
HRESULT WebBrowser:: ShowContextMenu(
DWORD dwID,
POINT *ppt,
IUnknown *pcmdtReserved,
IDispatch *pdispReserved){return E_NOTIMPL;}
HRESULT WebBrowser:: GetHostInfo(
DOCHOSTUIINFO *pInfo){return E_NOTIMPL;}
HRESULT WebBrowser:: ShowUI(
DWORD dwID,
IOleInPlaceActiveObject *pActiveObject,
IOleCommandTarget *pCommandTarget,
IOleInPlaceFrame *pFrame,
IOleInPlaceUIWindow *pDoc){return E_NOTIMPL;}
HRESULT WebBrowser:: HideUI( void){return E_NOTIMPL;}
HRESULT WebBrowser:: UpdateUI( void){return E_NOTIMPL;}
//HRESULT WebBrowser:: EnableModeless(
// BOOL fEnable){return E_NOTIMPL;}
HRESULT WebBrowser:: OnDocWindowActivate(
BOOL fActivate){return E_NOTIMPL;}
HRESULT WebBrowser:: OnFrameWindowActivate(
BOOL fActivate){return E_NOTIMPL;}
HRESULT WebBrowser:: ResizeBorder(
LPCRECT prcBorder,
IOleInPlaceUIWindow *pUIWindow,
BOOL fRameWindow){return E_NOTIMPL;}
HRESULT WebBrowser:: TranslateAccelerator(
LPMSG lpMsg,
const GUID *pguidCmdGroup,
DWORD nCmdID){return E_NOTIMPL;}
HRESULT WebBrowser:: GetOptionKeyPath(
LPOLESTR *pchKey,
DWORD dw){return E_NOTIMPL;}
HRESULT WebBrowser:: GetDropTarget(
IDropTarget *pDropTarget,
IDropTarget **ppDropTarget)
{
return E_NOTIMPL;//使用默认拖拽
//return S_OK;//自定义拖拽
}
HRESULT WebBrowser:: GetExternal( IDispatch **ppDispatch)
{
return E_NOTIMPL;
}
HRESULT WebBrowser:: TranslateUrl(
DWORD dwTranslate,
OLECHAR *pchURLIn,
OLECHAR **ppchURLOut){return E_NOTIMPL;}
HRESULT WebBrowser:: FilterDataObject(
IDataObject *pDO,
IDataObject **ppDORet){return E_NOTIMPL;}
*/
/*
===============
|Other Methods|
===============
*/
IWebBrowser2*
WebBrowser::GetWebBrowser2()
{
if( _pWB2 != NULL )
return _pWB2;
NULLTEST_SE( _pOleObj,L"Ole对象为空");
HRTEST_SE( _pOleObj->QueryInterface(IID_IWebBrowser2,(void**)&_pWB2),L"QueryInterface IID_IWebBrowser2 失败");
return _pWB2;
RETURN:
return NULL;
}
IHTMLDocument2*
WebBrowser::GetHTMLDocument2()
{
if( _pHtmlDoc2 != NULL )
return _pHtmlDoc2;
IWebBrowser2* pWB2 = NULL;
NULLTEST(pWB2 = GetWebBrowser2());//GetWebBrowser2已经将错误原因交给LastError.
IDispatch* pDp = NULL;
HRTEST_SE(pWB2->get_Document(&pDp),L"DWebBrowser2::get_Document 错误");
HRTEST_SE(pDp->QueryInterface(IID_IHTMLDocument2,(void**)&_pHtmlDoc2),L"QueryInterface IID_IHTMLDocument2 失败");
return _pHtmlDoc2;
RETURN:
return NULL;
}
IHTMLDocument3*
WebBrowser::GetHTMLDocument3()
{
if( _pHtmlDoc3 != NULL )
return _pHtmlDoc3;
IWebBrowser2* pWB2 = NULL;
NULLTEST(pWB2 = GetWebBrowser2());//GetWebBrowser2已经将错误原因交给LastError.
IDispatch* pDp = NULL;
HRTEST_SE(pWB2->get_Document(&pDp),L"DWebBrowser2::get_Document 错误");
HRTEST_SE(pDp->QueryInterface(IID_IHTMLDocument3,(void**)&_pHtmlDoc3),L"QueryInterface IID_IHTMLDocument3 失败");
return _pHtmlDoc3;
RETURN:
return NULL;
}
IHTMLWindow2*
WebBrowser::GetHTMLWindow2()
{
if( _pHtmlWnd2 != NULL)
return _pHtmlWnd2;
IHTMLDocument2* pHD2 = GetHTMLDocument2();
NULLTEST( pHD2 );
HRTEST_SE( pHD2->get_parentWindow(&_pHtmlWnd2),L"IHTMLWindow2::get_parentWindow 错误" );
return _pHtmlWnd2;
RETURN:
return NULL;
}
IHTMLEventObj*
WebBrowser::GetHTMLEventObject()
{
if( _pHtmlEvent != NULL )
return _pHtmlEvent;
IHTMLWindow2* pHW2;
NULLTEST( pHW2 = GetHTMLWindow2() );
HRTEST_SE( pHW2->get_event(&_pHtmlEvent),L"IHTMLWindow2::get_event 错误");
return _pHtmlEvent;
RETURN:
return NULL;
}
BOOL
WebBrowser::SetWebRect(LPRECT lprc)
{
BOOL bRet = FALSE;
if( false == _bInPlaced )//尚未OpenWebBrowser操作,直接写入_rcWebWnd
{
_rcWebWnd = *lprc;
}
else//已经打开WebBrowser,通过 IOleInPlaceObject::SetObjectRects 调整大小
{
SIZEL size;
size.cx = RECTWIDTH(*lprc);
size.cy = RECTHEIGHT(*lprc);
IOleObject* pOleObj;
NULLTEST( pOleObj= _GetOleObject());
HRTEST_E( pOleObj->SetExtent( 1,&size ),L"SetExtent 错误");
IOleInPlaceObject* pInPlace;
NULLTEST( pInPlace = _GetInPlaceObject());
HRTEST_E( pInPlace->SetObjectRects(lprc,lprc),L"SetObjectRects 错误");
_rcWebWnd = *lprc;
}
bRet = TRUE;
RETURN:
return bRet;
}
BOOL
WebBrowser::OpenWebBrowser()
{
BOOL bRet = FALSE;
NULLTEST_E( _GetOleObject(),L"ActiveX对象为空" );//对于本身的实现函数,其自身承担错误录入工作
if( (RECTWIDTH(_rcWebWnd) && RECTHEIGHT(_rcWebWnd)) == 0 )
::GetClientRect( GetHWND() ,&_rcWebWnd);//设置WebBrowser的大小为窗口的客户区大小.
if( _bInPlaced == false )// Activate In Place
{
_bInPlaced = true;//_bInPlaced must be set as true, before INPLACEACTIVATE, otherwise, once DoVerb, it would return error;
_bExternalPlace = 0;//lParam;
HRTEST_E( _GetOleObject()->DoVerb(OLEIVERB_INPLACEACTIVATE,0,this,0, GetHWND() ,&_rcWebWnd),L"关于INPLACE的DoVerb错误");
_bInPlaced = true;
//* 挂接DWebBrwoser2Event
IConnectionPointContainer* pCPC = NULL;
IConnectionPoint* pCP = NULL;
HRTEST_E( GetWebBrowser2()->QueryInterface(IID_IConnectionPointContainer,(void**)&pCPC),L"枚举IConnectionPointContainer接口失败");
HRTEST_E( pCPC->FindConnectionPoint( DIID_DWebBrowserEvents2,&pCP),L"FindConnectionPoint失败");
DWORD dwCookie = 0;
HRTEST_E( pCP->Advise( (IUnknown*)(void*)this,&dwCookie),L"IConnectionPoint::Advise失败");
}
bRet = TRUE;
RETURN:
return bRet;
}
BOOL
WebBrowser::OpenURL(VARIANT* pVarUrl)
{
BOOL bRet = FALSE;
HRTEST_E( GetWebBrowser2()->Navigate2( pVarUrl,0,0,0,0),L"GetWebBrowser2 失败");
bRet = TRUE;
RETURN:
return bRet;
}
五.例子
class WebBrowserWindow :
public WebBrowser
{
public:
WebBrowserWindow(void);
~WebBrowserWindow(void);
public:
virtual HWND GetHWND(){ return YourWindow};
private:
LRESULT OnCreate( WPARAM wParam,LPARAM lParam);
//您可以在OnCreate中调用OpenWebBrowser 然后调用 OpenURL
};
六.下期预告,如果有的话
如果有人感兴趣的话, 以后会说道如何扩展WebBrowser, 让他包罗万象, 你会发现很简单.
如果有问题,欢迎提问. 谢谢.
相关文章推荐
- 使用CreateThread 时报错,函数调用缺少参数列表
- C语言陷阱解析--I Do Not Know C
- C与C++函数声明时的几个小的语法差异
- superwebbench
- C++Primer 1.2章笔记整理
- C语言实现动态字符串,并且统计大小写
- Rotate Array
- C语言通过串口发送AT指令
- C++ vector【基础】
- C/C++开发者必不可少的15款编译器+IDE
- 【心酸历程】atom上打造C/C++的ide
- C++中引用和匿名对象的理解和本质剖析
- C++ 引用外部 DLL
- 什么是预编译?何时需要预编译 (C、C++工程师面试题)
- Android之JNI NDK错误 c/c++ indexer has encountered a problem, An internal error occurred during xx
- 定义、声明、初始化、赋值 小记(C++)
- 图的最小生成树 Prim算法 C++实现
- c++中赋值与初始赋值的区别
- C++的Json解析库:jsoncpp和boost
- C/C++ 中static的常见作用