【IE编程】 IWebBrowser2子线程调用崩溃

本文介绍了一种在C++中调用JavaScript函数的方法,特别关注了从子线程调用JS函数时可能遇到的问题及解决方案。通过使用COM接口的序列化和反序列化(Marshalling和Unmarshalling),文章详细描述了如何避免线程间直接传递COM对象导致的崩溃,并提供了完整的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.c++调用js函数方法:

#include <Mshtml.h>

bool CBrowserFunc::ExecJsFun(IWebBrowser2 *pWebBrowser2, const std::wstring& lpJsFun, const std::wstring &json_param)
{
	//程序开始处com是否已经初始化
	if (NULL == pWebBrowser2)
		return false;
	CComPtr<IDispatch> pDoc;
	HRESULT hr = pWebBrowser2->get_Document(&pDoc);
	if (FAILED(hr))
		return false;
	CComQIPtr<IHTMLDocument2> pDoc2 = pDoc;
	if (NULL == pDoc2)
		return false;
	CComQIPtr<IDispatch> 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));

	dispParams.cArgs = 1;
	dispParams.rgvarg = new VARIANT;
	CComBSTR bstr(json_param.c_str());
	bstr.CopyTo(&dispParams.rgvarg[0].bstrVal);
	dispParams.rgvarg[0].vt = VT_BSTR;
	
	EXCEPINFO execInfo;
	memset(&execInfo, 0, sizeof(EXCEPINFO));
	VARIANT vResult;
	UINT uArgError = (UINT)-1;
	hr = pScript->Invoke(id, IID_NULL, 0, DISPATCH_METHOD, &dispParams, &vResult, &execInfo, &uArgError);
	delete[] dispParams.rgvarg;
	if (FAILED(hr))
		return false;

	return true;
}

该方法在主线程中调用没问题,可以访问js函数,但是如果子线程调用就会崩溃,崩溃点为:

hr = pDoc2->get_Script(&pScript);

经查阅资料发现下面这句话:

In order to pass a COM interface pointer between threads, you need to do marshalling. See the following functions on MSDN:

CoMarshalInterThreadInterfaceInStream

CoGetInterfaceAndReleaseStream

=>方法:主线程将通过CoMarshalInterThreadInterfaceInStream将IHTMLDocument2得到IStream,然后子线程传递IStream再通过CoGetInterfaceAndReleaseStream将IStream转成IHTMLDocument2

即传递参数使用IStream

最终经改进得出最终代码:

第一步:成员变量
IWebBrowser2* browser2_obj_;
IStream* browser2_stream_;

第二步:
void DocumentComplete(IDispatch *pDisp, VARIANT* &url);
在页面加载完成初始化IStream
void CMainWindow::DocumentComplete(IDispatch *pDisp, VARIANT* &url)
{
	if (pDisp == browser2_obj_) {
		//设置缩放始终为100,解决设置系统缩放比为125及以下时,浏览器自动放大导致显示不全问题
		base::win::ScopedVariant varScaleDefault(100);
		browser2_obj_->ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, varScaleDefault.AsInput(), nullptr);

		//加载完成后才能初始化pStream_
		CComPtr<IDispatch> pHtmlDocDisp;
		HRESULT hr = browser2_obj_->get_Document(&pHtmlDocDisp);
		if (FAILED(hr))
			return;
		CComPtr<IHTMLDocument2> pHtmlDoc2;
		hr = pHtmlDocDisp->QueryInterface(IID_IHTMLDocument2, (void**)& pHtmlDoc2);
		if (FAILED(hr) || (NULL == pHtmlDoc2)) {
			return;
		}

		hr = CoMarshalInterThreadInterfaceInStream(IID_IHTMLDocument2, pHtmlDoc2, &browser2_stream_);
		if (FAILED(hr)) {
			return;
		}
	}
}


第3步:
bool CBrowserFunc::ExecJsFun2(IStream* pStream_, const std::wstring& lpJsFun, const std::wstring &json_param)
{
	//程序开始处com是否已经初始化
	if (NULL == pStream_)
		return false;

	//不加这句子线程调用会崩溃 
	CoInitialize(NULL);
	
	HRESULT hr;
	static IHTMLDocument2* pDoc2 = NULL;//不加static多次调用会崩溃
	if (pDoc2 == NULL){
		hr = CoGetInterfaceAndReleaseStream((IStream*)pStream_, IID_IHTMLDocument2, (void**)&pDoc2);
	}

	if (NULL == pDoc2)
		return false;

	CComQIPtr<IDispatch> 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));

	dispParams.cArgs = 1;
	dispParams.rgvarg = new VARIANT;
	CComBSTR bstr(json_param.c_str());
	bstr.CopyTo(&dispParams.rgvarg[0].bstrVal);
	dispParams.rgvarg[0].vt = VT_BSTR;

	EXCEPINFO execInfo;
	memset(&execInfo, 0, sizeof(EXCEPINFO));
	VARIANT vResult;
	UINT uArgError = (UINT)-1;
	hr = pScript->Invoke(id, IID_NULL, 0, DISPATCH_METHOD, &dispParams, &vResult, &execInfo, &uArgError);
	delete[] dispParams.rgvarg;
	if (FAILED(hr))
		return false;

	return true;
}

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kevin--你不知道的事

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值