【IE編程】 IWebBrowser2子線程調用崩潰

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;
}

 

 

 

 

 

 

 

 

 

 

發佈了45 篇原創文章 · 獲贊 21 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章