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