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