目的:加深对IWebBrowser2的理解,整理。方便以后学习和使用。也方便需要这反面的朋友少点弯路。
声明:有些知识点是本人在使用和学习中借助网络搜索到的,所以难免会有雷同,我会尽量标注原著的出处,当然也可能找不到原著的出去了,如果牵扯到版权或者其他的可以通知我,我会跟你一同处理。本文的源码也有部分是来自网络和msdn上面,出处我也会尽量标明。
前言:前段时间,一个大学同学问我,在win下面c++怎么获取web的登陆状态,是哪个用户登陆的,我告诉他可以内嵌webbrowser实现,他叫我帮忙提供一个demo给他,所以我利用空余时间帮忙做了一个win32的程序模拟登陆csdn的demo给他。后面他又说,不要模拟登陆,要用户自己点击登陆,然后c++来获取登陆是否成功,并且是哪个用户在登陆。我说那就得改改程序,给他提了几个点。最后也不知道他们公司的c++程序员 搞定没。后面自己想了一下,觉得有必要把c++与IWebBrowser2的东西整理一下,自己也理一下,所以用了点时间添加了一个demo,并且写这篇博客。
正文
1.开发环境和知识点
代码是使用vs2008编译,是win32的程序(不是mfc)。阅读的时候需要你了解过简单的win32编程,也会一些简单的web基础(html元素的概念),会根据ie浏览器查询你需要的html元素。如果你具备这些 那么阅读和学习这个东西就很简单了。
2.引用和借鉴的url
这个我会尽量列举在这里,找不到出去就没法发了
http://www.tuicool.com/articles/fiq26ve (win32使用iwebbrowser)
https://msdn.microsoft.com/en-us/library/bb508508(VS.85).aspx (html事件监听)
http://bbs.csdn.net/topics/20135139 (IDocHostUIHandler重写)
http://blog.csdn.net/mfcing/article/details/44540683 http://blog.csdn.net/aasmfox/article/details/7016236 (c++与js交互)
http://www.cnblogs.com/jivi/archive/2013/03/28/2985726.html (源码借鉴了 HTMLElementEvents2 的重写)
以上就是我能记住的参考过的url了。下面的文章不在注明具体是那个知识点参照上面的url了。
3.win32模拟登陆csdn程序
原理说明:内嵌一个webbrowser的com组件,指定url就会把页面加载出来(具体原理需要http通信,获取返回值,解析页面,执行js等等),加载之后我们根据html标签的id或者是name或者是tag等查找你需要的IHTMLElement对象(id和name的查找,可以用ie打开url按f12键查找元素,查看html标签具体信息),接下来就是对IHTMLElement的操作,可以输入数据,触发点击事件等。点击之后等待页面跳转或者是重新加载,根据加载的新数据来判断是否登录成功(一般登录成功会跳转页面,有些web会记录cookie等其他信息,这些都需要根据不同的web网站 做不同的判断)
首先,需要一个基于对话框的win32程序,上面有2个按钮(一个模拟登陆按钮,一个导航按钮)和3个输入框(一个输入url,一个输入用户名,一个输入密码)
代码段
int WINAPI _tWinMain(HINSTANCE hInst, HINSTANCE h0, LPTSTR lpCmdLine, int nCmdShow)
{
DialogBox(hInst,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,(DLGPROC)MainDialogProc);
return TRUE;
}
// Windows 事件处理
LRESULT CALLBACK MainDialogProc(HWND hDlg,UINT message,
WPARAM wParam,LPARAM lParam)
{
//消息的处理,我想你要的就是这里了
switch(message)
{
case WM_INITDIALOG:
return DialogInit(hDlg,wParam,lParam);
break;
case WM_COMMAND:
return CommandFun(hDlg,wParam,lParam);
break;
case WM_CLOSE://关闭在这里
EndDialog(hDlg,TRUE);
return TRUE;
break;
}
return FALSE;
}
LRESULT DialogInit(HWND hDlg,WPARAM wParam,LPARAM lParam)
{
RECT rc;
GetClientRect(hDlg, &rc);
HWND hStaticOption = GetDlgItem(hDlg,IDC_STATIC_OPTION);
RECT rc_Option;
GetWindowRect(hStaticOption,&rc_Option);
//
SetDlgItemText(hDlg,IDC_EDIT_URL,_T("https://passport.csdn.net/account/login"));
SetDlgItemText(hDlg,IDC_EDIT_NAME,_T("xxxxxxx"));
SetDlgItemText(hDlg,IDC_EDIT_PWD,_T("xxxxxx"));
//
RECT webRc;
webRc.left = 0;
webRc.top = rc_Option.bottom;
webRc.right = rc.right;
webRc.bottom = rc.bottom;
gWebAutoLogin = new WebAutoLogin(hDlg, webRc);
gWebAutoLogin->Navigate(L"https://www.baidu.com/");
StartWaitWebLoad(hDlg);
return TRUE;
}
LRESULT CommandFun(HWND hDlg,WPARAM wParam,LPARAM lParam)
{
//IDC_CLOSE 是我在对话框中加入的一个按钮的ID 也可以关闭
if (LOWORD(wParam)==IDOK)
{
PostQuitMessage(0);
}
else if (LOWORD(wParam)==IDC_BTN_GO)
{
LoadWeb(hDlg);
}
else if (LOWORD(wParam)==IDC_BTN_LOGIN)
{
SimulateLogin(hDlg);
}
else
{
return FALSE;
}
return TRUE;
}
void LoadWeb(HWND hDlg)
{
wchar_t url[MAX_PATH*2+1] ={0};
GetDlgItemText(hDlg,IDC_EDIT_URL,url,MAX_PATH*2);
std::wstring strUrl;
strUrl.assign(url);
gWebAutoLogin->Navigate(strUrl);
StartWaitWebLoad(hDlg);
}
void SimulateLogin(HWND hDlg)
{
wchar_t name[MAX_PATH+1] ={0};
GetDlgItemText(hDlg,IDC_EDIT_NAME,name,MAX_PATH);
wchar_t pswd[MAX_PATH+1] ={0};
GetDlgItemText(hDlg,IDC_EDIT_PWD,pswd,MAX_PATH);
std::wstring userName;
userName.assign(name);
std::wstring password;
password.assign(pswd);
if (gWebAutoLogin->AutoLogin(hDlg,userName,password))
{
bIsSimulateLogin = TRUE;
StartWaitWebLoad(hDlg);
}
}
这里面主要是用gWebAutoLogin对象,这个是简单封装了的一个WebAutoLogin类。
下面就是 WebAutoLogin类的实现主代码片段
WebAutoLogin::WebAutoLogin(HWND hwnd,RECT webRc)
{
LPOLESTR pszName=OLESTR("shell.Explorer.2");
m_WinContainer.Create(hwnd, webRc, 0,WS_CHILD |WS_VISIBLE);
m_WinContainer.CreateControl(pszName);
HRESULT hr = m_WinContainer.QueryControl(__uuidof(IWebBrowser2),(void**)&m_iWebBrowser);
if(FAILED(hr))
{
MessageBox(hwnd,_T("获取IWebBrowser2 对象失败!!!"),_T("错误"),MB_OK|MB_ICONERROR);
m_iWebBrowser = NULL;
}
}
WebAutoLogin::~WebAutoLogin(void)
{
if (NULL != m_iWebBrowser)
{
m_iWebBrowser->Release();
}
}
READYSTATE WebAutoLogin::ReadyState()
{
READYSTATE r = READYSTATE_UNINITIALIZED;
HRESULT hr = m_iWebBrowser->get_ReadyState(&r);
//printf("get_ReadyState = %d",r);
if (SUCCEEDED(hr) && r == READYSTATE_COMPLETE)
{
}
return r;
}
bool WebAutoLogin::AutoLogin(HWND hwnd,std::wstring userName,std::wstring password)
{
bool isLogin = false;
HRESULT hr = S_OK;
IHTMLElement *user_nameElet= GetHTMLElementByIdOrName(L"username");
if (user_nameElet!=0)
{
//转换成CComBSTR
CComBSTR bStr =userName.c_str();
//输入内容
hr = user_nameElet->put_innerText(bStr);
user_nameElet->Release();
}
else
{
MessageBox(hwnd,_T("获取:用户名HTMLElement 失败!"),_T("错误"),MB_OK|MB_ICONERROR );
}
IHTMLElement *passwdElet= GetHTMLElementByIdOrName(L"password");
if (passwdElet!=0)
{
//转换成CComBSTR
CComBSTR bStr = password.c_str();
//输入内容
hr = passwdElet->put_innerText(bStr);
passwdElet->Release();
}
else
{
MessageBox(hwnd,_T("获取:密码HTMLElement 失败!"),_T("错误"),MB_OK|MB_ICONERROR );
}
IHTMLElement *loginSubElet = GetHTMLElementByTag(L"input",L"value",L"登 录");
if (loginSubElet!=0)
{
loginSubElet->click();
loginSubElet->Release();
isLogin = true;
}
else
{
MessageBox(hwnd,_T("获取:登陆HTMLElement 失败!"),_T("错误"),MB_OK|MB_ICONERROR );
}
return isLogin;
}
bool WebAutoLogin::LoginResult()
{
bool isLogin = false;
IDispatch *dispatch=0;
HRESULT hr = m_iWebBrowser->get_Document(&dispatch);
if ((S_OK==hr)&&(dispatch!=0))
{
IHTMLDocument2 *doc;
hr = dispatch->QueryInterface(IID_IHTMLDocument2,(void**)&doc);
dispatch->Release();
if ( S_OK == hr )
{
//登陆成功的 判断方式可以用不同的 方法
BSTR bstrCookie;
hr = doc->get_cookie(&bstrCookie);
if (S_OK == hr)
{
_bstr_t bstr_t(bstrCookie);
std::string strCookie(bstr_t);
::SysFreeString(bstrCookie);
}
BSTR bstrReferrer;
hr = doc->get_referrer(&bstrReferrer);
if (S_OK == hr)
{
if(NULL != bstrReferrer)
{
_bstr_t bstr_t0(bstrReferrer);
std::string strReferrer(bstr_t0);
::SysFreeString(bstrReferrer);
}
}
BSTR bstrUrl;
hr = doc->get_URL(&bstrUrl);
if (S_OK == hr)
{
if(NULL != bstrUrl)
{
_bstr_t bstr_t(bstrUrl);
std::string strUrl(bstr_t);
if (0 == strcmp("http://www.csdn.net/",strUrl.c_str()))
{
isLogin = true;
}
// free the BSTR
::SysFreeString(bstrUrl);
}
}
}
doc->Release();
}
dispatch->Release();
return isLogin;
}
void WebAutoLogin::Navigate(std::wstring strUrl)
{
VARIANT varMyURL;
VariantInit(&varMyURL);
varMyURL.vt = VT_BSTR;
varMyURL.bstrVal = SysAllocString(strUrl.c_str());
m_iWebBrowser-> Navigate2(&varMyURL,0,0,0,0);
SysFreeString(varMyURL.bstrVal);
VariantClear(&varMyURL);
}
IHTMLElement * WebAutoLogin::GetHTMLElementByTag(std::wstring tagName,std::wstring PropertyName,
std::wstring macthValue)
{
IHTMLElement *retElement=0;
IDispatch *dispatch=0;
HRESULT hr = m_iWebBrowser->get_Document(&dispatch);
if ((S_OK==hr)&&(0 != dispatch))
{
IHTMLDocument2 *doc;
dispatch->QueryInterface(IID_IHTMLDocument2,(void**)&doc);
dispatch->Release();
IHTMLElementCollection* doc_all;
hr = doc->get_all(&doc_all); // this is like doing document.all
if (S_OK == hr)
{
VARIANT vKey;
vKey.vt=VT_BSTR;
vKey.bstrVal=SysAllocString(tagName.c_str());
VARIANT vIndex;
VariantInit(&vIndex);
hr = doc_all->tags(vKey,&dispatch); // this is like doing document.all["messages"]
//清理
SysFreeString(vKey.bstrVal);
VariantClear(&vKey);
VariantClear(&vIndex);
if ((S_OK == hr) && (0 != dispatch))
{
CComQIPtr< IHTMLElementCollection > all_tags = dispatch;
//hr = dispatch->QueryInterface(IHTMLElementCollection,(void **)&all_tags); // it's the caller's responsibility to release
if (S_OK == hr)
{
long nTagsCount=0; //
hr = all_tags->get_length( &nTagsCount);
if ( FAILED( hr ) )
{
return retElement;
}
for(long i=0; iitem( CComVariant(i), CComVariant(i), &spInputElement );
if ( FAILED( hr ) )
continue;
CComVariant vValue;
hr = spInputElement.GetPropertyByName(PropertyName.c_str(), &vValue );
if (VT_EMPTY != vValue.vt)
{
LPCTSTR lpValue = vValue.bstrVal?
OLE2CT( vValue.bstrVal ) : NULL;
if(NULL == lpValue)
continue;
std::wstring cs = (LPCTSTR)lpValue;
if (0 == _tcscmp(cs.c_str(),macthValue.c_str()))
{
hr = spInputElement->QueryInterface(IID_IHTMLElement,(void **)&retElement);
if (S_OK == hr)
{
}
else
{
retElement = 0;
}
break;
}
}
//
//CComVariant vName,vVal,vType; //名,值,类型
//hr = spInputElement.GetPropertyByName( L"name", &vName );
//if( FAILED( hr ) ) continue;
//hr = spInputElement.GetPropertyByName( L"value", &vVal );
//if( FAILED( hr ) ) continue;
//hr = spInputElement.GetPropertyByName( L"type", &vType );
//if( FAILED( hr ) ) continue;
//LPCTSTR lpName = vName.bstrVal?
// OLE2CT( vName.bstrVal ) : _T("NULL"); //未知域名
//LPCTSTR lpVal = vVal.bstrVal?
// OLE2CT( vVal.bstrVal ) : _T("NULL"); //空值,未输入
//LPCTSTR lpType = vType.bstrVal?
// OLE2CT( vType.bstrVal ) : _T("NULL"); //未知类型
}
}
else
{
retElement = 0;
}
dispatch->Release();
}
doc_all->Release();
}
doc->Release();
}
return retElement;
}
IHTMLElement * WebAutoLogin::GetHTMLElementByIdOrName(std::wstring idorName)
{
IHTMLElement *retElement=0;
IDispatch *dispatch=0;
HRESULT hr = m_iWebBrowser->get_Document(&dispatch);
if ((S_OK==hr)&&(0!=dispatch))
{
IHTMLDocument2 *doc;
dispatch->QueryInterface(IID_IHTMLDocument2,(void**)&doc);
dispatch->Release();
IHTMLElementCollection* doc_all;
hr = doc->get_all(&doc_all); // this is like doing document.all
if (S_OK == hr)
{
VARIANT vKey;
vKey.vt=VT_BSTR;
vKey.bstrVal=SysAllocString(idorName.c_str());
VARIANT vIndex;
VariantInit(&vIndex);
hr = doc_all->item(vKey,vIndex,&dispatch); // this is like doing document.all["messages"]
//清理
SysFreeString(vKey.bstrVal);
VariantClear(&vKey);
VariantClear(&vIndex);
if ((S_OK == hr) && (0 != dispatch))
{
hr = dispatch->QueryInterface(IID_IHTMLElement,(void **)&retElement); // it's the caller's responsibility to release
if (S_OK == hr)
{
}
else
{
retElement = 0;
}
dispatch->Release();
}
doc_all->Release();
}
doc->Release();
}
return retElement;
}
到这里 一个模拟登陆csdn的就已经可以了,但是还是有几个问题:1.页面加载完成的判断是用定时器查询的这种处理不太好。2.这个需要把用户名和账号输入到win32的界面,而不是web页面(当然这也是为了自动登陆)。3.c++执行js或者是js能否执行c++函数,这个demo里面没有实现
4.IWebBrowser2的事件获取以及c++与js的交互程序
原理说明:通过继承IDispatch(或者是HTMLElementEvents2,DWebBrowserEvents2)实现对应的接口,来获取IWebBrowser2的事件以及HTMLElement的事件(这个的具体原理得到msdn上面去找了,这里就不细说了)。通过继承IDocHostUIHandler(这个接口内容很丰富,不止js调用c++这点功能)来实现js调用c++的函数(具体原理这里也不展开说了)
下面贴一下主代码,win32程序就不贴了和上一个demo差不多。
class WebBrowserSink: public DWebBrowserEvents2 这个类主要是监听web的加载是否完成事件,以及下载进度事件,状态等信息等事件。
STDMETHODIMP WebBrowserSink::Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS *pDispParams,VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr)
{
UNREFERENCED_PARAMETER(lcid);
UNREFERENCED_PARAMETER(wFlags);
UNREFERENCED_PARAMETER(pVarResult);
UNREFERENCED_PARAMETER(pExcepInfo);
UNREFERENCED_PARAMETER(puArgErr);
if(!IsEqualIID(riid,IID_NULL))
return DISP_E_UNKNOWNINTERFACE; // riid should always be IID_NULL
//LogTrace(L"WebBrowserSink::Invoke dispIdMember = %d",dispIdMember);
switch (dispIdMember)
{
case DISPID_BEFORENAVIGATE2:
if (NULL != m_pEventCallBack)
{
m_pEventCallBack->OnBeforeNavigate2(
(IDispatch*)pDispParams->rgvarg[6].byref,
(VARIANT*)pDispParams->rgvarg[5].pvarVal,
(VARIANT*)pDispParams->rgvarg[4].pvarVal,
(VARIANT*)pDispParams->rgvarg[3].pvarVal,
(VARIANT*)pDispParams->rgvarg[2].pvarVal,
(VARIANT*)pDispParams->rgvarg[1].pvarVal,
(VARIANT_BOOL*)pDispParams->rgvarg[0].pboolVal
);
}
break;
case DISPID_DOCUMENTCOMPLETE:
if (NULL != m_pEventCallBack)
{
m_pEventCallBack->OnDocumentComplete(
(IDispatch*)pDispParams->rgvarg[0].byref,
pDispParams->rgvarg[0].pvarVal->bstrVal
);
}
break;
case DISPID_PROGRESSCHANGE:
if (NULL != m_pEventCallBack)
{
}
break;
case DISPID_STATUSTEXTCHANGE:
if (NULL != m_pEventCallBack)
{
m_pEventCallBack->OnStatusTextChange(
(IDispatch*)pDispParams->rgvarg[0].byref,
pDispParams->rgvarg[0].bstrVal
);
}
break;
default:
break;
}
return S_OK;
}
其他地方调用的方式为:
void WebMonitor::RegisterIeEventDealer()
{
HRESULT hr;
// 声明一个IConnectionPointContainer和IConnectionPoint实例。
CComPtr spConnectionPointContainer;
CComPtr spConnectionPointBrowserEvents;
// pWebBrowser2->QueryInterface(IID_IConnectionPointContainer,(void**)&spConnectionPointContainer);
// 利用 IWebBrowser2 接口的 QueryInterface 方法获得 IConnectionPointContainer 接口
m_piWebBrowser->QueryInterface(IID_IConnectionPointContainer,(void**)&spConnectionPointContainer);
// 利用 IConnectionPointContainer 接口的 FindConnectionPoint 获取 IID为DIID_DWebBrowserEvents2 的连接点
spConnectionPointContainer->FindConnectionPoint(DIID_DWebBrowserEvents2,&spConnectionPointBrowserEvents);
// 利用IID为DIID_DWebBrowserEvents2的连接点的Advise建立一个实现了DWebBrowserEvents2接口的接收器的实例和此连接点的连接。
// 第一个参数就是接收器的实例,必须是一个实现了DWebBrowserEvents2接口的类的实例
// 在这里我们设置成this,也就是自己实现了DWebBrowserEvents2接口,这个是通过继承CWebEventSink实现的
m_dwCookie = 0;
m_pWebBrowserSink = new WebBrowserSink(this);
hr = spConnectionPointBrowserEvents->Advise(m_pWebBrowserSink,&m_dwCookie);
if (SUCCEEDED(hr))
{
// Successfully advised
}
}
class HtmlElementSink: public HTMLElementEvents2这个类是对指定的 HTMLElement 对象进行事件的监听。
STDMETHODIMP HtmlElementSink::Invoke(DISPID dispidMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pdispparams,
VARIANT* pvarResult,
EXCEPINFO* pexcepinfo,
UINT* puArgErr)
{
switch (dispidMember)
{
case DISPID_HTMLELEMENTEVENTS2_ONCLICK:
if (NULL != m_pEventCallBack)
{
m_pEventCallBack->OnClick();
}
break;
default:
break;
}
return S_OK;
}
调用方式,需要先获取到指定的IHTMLElement *,再进行监听绑定。
//监听 页面事件
void WebMonitor::ConnectHTMLElementEvent(IHTMLElement* pElem)
{
if (NULL == pElem)
{
return;
}
HRESULT hr;
IConnectionPointContainer* pCPC = NULL;
IConnectionPoint* pCP = NULL;
DWORD dwCookie;
// Check that this is a connectable object.
hr = pElem->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);
if (SUCCEEDED(hr))
{
// 枚举 查看一下 支持的 ConnectionPoint
//IEnumConnectionPoints *pECPS = NULL;
//hr = pCPC->EnumConnectionPoints(&pECPS);
//if (SUCCEEDED(hr))
//{
// IConnectionPoint* tmppCP = NULL;
// ULONG cFetched = 1;
// IID guid;
// while(SUCCEEDED(hr = pECPS->Next(1,&tmppCP,&cFetched))
// &&(cFetched>0))
// {
// tmppCP->GetConnectionInterface(&guid);
// }
//}
// Find the connection point.
//hr = pCPC->FindConnectionPoint(DIID_HTMLElementEvents2, &pCP);
hr = pCPC->FindConnectionPoint(DIID_HTMLButtonElementEvents, &pCP);
if (SUCCEEDED(hr))
{
// Advise the connection point.
// pUnk is the IUnknown interface pointer for your event sink
if(NULL == m_pHtmlElementSink)
{
m_pHtmlElementSink = new HtmlElementSink(this);
}
hr = pCP->Advise(m_pHtmlElementSink, &dwCookie);
if (SUCCEEDED(hr))
{
// Successfully advised
}
pCP->Release();
}
pCPC->Release();
}
}
接下来就是c++调用js函数了,这个代码比较简单,给定一个js函数名称和参数就可以利用 IHTMLDocument2 的JavaScript接口的invoke方法调用。
bool WebMonitor::ExecJsFun( const std::wstring& lpJsFun,const std::vector& params )
{
if ( NULL == m_piWebBrowser )
return false;
CComPtr pDoc;
HRESULT hr = m_piWebBrowser->get_Document(&pDoc);
if ( FAILED(hr) )
return false;
CComQIPtr pDoc2=pDoc;
if ( NULL == pDoc2 )
return false;
CComQIPtr pScript;
hr = pDoc2->get_Script(&pScript);
if ( FAILED(hr) )
return false;
DISPID id = NULL;
CComBSTR bstrFun(lpJsFun.c_str());
hr = pScript->GetIDsOfNames(IID_NULL, &bstrFun, 1, LOCALE_SYSTEM_DEFAULT, &id);
if ( FAILED(hr) )
return false;
DISPPARAMS dispParams;
memset(&dispParams, 0, sizeof(DISPPARAMS));
int nParamCount = params.size();
if (nParamCount > 0)
{
dispParams.cArgs =nParamCount;
dispParams.rgvarg =new VARIANT[nParamCount];
for (int i=0; iInvoke(id, IID_NULL, 0, DISPATCH_METHOD, &dispParams, &vResult, &execInfo, &uArgError);
delete[] dispParams.rgvarg;
if ( FAILED(hr) )
return false;
return true;
}
js调用c++函数,这个需要先继承重写IDocHostUIHandler并且需要在js代码用 window.external.CppCall(10);方式去调用。
主要实现IDocHostUIHandler 的 virtual HRESULT STDMETHODCALLTYPE GetExternal( /* [out] */ IDispatch **ppDispatch) = 0;方法,返回一个继承IDispatch的ClientCall对象,ClientCall里面需要实现对c++函数的id指定,以及调用参数的处理等。
下面是一些主要代码,这是注册 TDocHostUIHandlerImpl对象。
void WebMonitor::RegisterUIHandlerToJs()
{
ICustomDoc *m_spCustDoc;
HRESULT hr;
CComPtr pDoc;
hr = m_piWebBrowser->get_Document(&pDoc);
if ( FAILED(hr) )
return;
CComQIPtr pDoc2=pDoc;
if ( NULL == pDoc2 )
return ;
hr = pDoc2-> QueryInterface(IID_ICustomDoc,(void**)&m_spCustDoc);
if(SUCCEEDED(hr))
{
if (NULL == m_pDocHostUIHandler)
{
m_pDocHostUIHandler = new TDocHostUIHandlerImpl();
}
hr = m_spCustDoc-> SetUIHandler(m_pDocHostUIHandler);
if (SUCCEEDED(hr))
{
// Successfully advised
}
}
}
ClientCall的主要代码片段:
class ClientCall:public IDispatch
{
HRESULT _stdcall GetIDsOfNames(
REFIID riid,
OLECHAR FAR* FAR* rgszNames,
unsigned int cNames,
LCID lcid,
DISPID FAR* rgDispId
)
{
if(lstrcmp(rgszNames[0], L"CppCall")==0)
{
//网页调用window.external.CppCall时,会调用这个方法获取CppCall的ID
*rgDispId = 100;
}
return S_OK;
}
HRESULT _stdcall Invoke(
DISPID dispIdMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS* pDispParams,
VARIANT* pVarResult,
EXCEPINFO* pExcepInfo,
unsigned int* puArgErr
)
{
if(dispIdMember == 100)
{
//网页调用CppCall时,或根据获取到的ID调用Invoke方法
CppCall(pDispParams->rgvarg[0].intVal);
}
return S_OK;
}
}
以上就是主要的代码段和一些说明。
5.后记
需要完整代码的可以下载:http://download.csdn.net/detail/nanjun520/9674092 不需要积分,有csdn账号就行。
目前的demo都是在主ui线程里面执行,这个在网络不好的情况下会阻塞主ui,大家在使用的时候可以考虑添加线程来解决,也可以查找一下是否有其他的参数设置可以解决这个问题。
IWebBrowser2的功能挺多的,本文只是挑选了几个 个人感觉常用的功能,其他的功能大家可以去深挖一下,IWebBrowser2->Navigate2 函数也可以配置post数据和参数,不过一般比较少这样用。
作为码农,需要学习的东西挺多的,基于兴趣来学习还是比较有动力的。毕竟程序是码农创造的,身为码农,大家一起努力。 文章就写到这来,有问题欢迎大家一起讨论,如果文章或者是源码有错误 欢迎指正。