c++内嵌IWebBrowser2功能整理

目的:加深对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数据和参数,不过一般比较少这样用。

作为码农,需要学习的东西挺多的,基于兴趣来学习还是比较有动力的。毕竟程序是码农创造的,身为码农,大家一起努力。 文章就写到这来,有问题欢迎大家一起讨论,如果文章或者是源码有错误 欢迎指正。

你可能感兴趣的:(c++内嵌IWebBrowser2功能整理)