C++ call JavaScript,介绍2种解决方案:1种嵌Webkit作浏览器(纯QT方法);另1种嵌IE控件作为浏览器(适用于非QT,但我是在QT里使用的)。
这个需求比较冷门,所以资料少,搞起来比较折腾人。
解放方案1:使用Webkit library (可以说是纯QT实现)
代码量不多,直接贴代码 (读起来一点不痛苦的) :
myWebView = new QWebView(this); //this 是main window widget, myWebView 是它的成员变量
myWebView->page()->settings()->setAttribute(QWebSettings::JavascriptEnabled, true);
myWebView->page()->settings()->setAttribute(QWebSettings::PluginsEnabled,true)
myWebView->page()->mainFrame()->addToJavaScriptWindowObject("mainWindowObject", this); //html页面中,可以通过"mainWindowObject"这个对象名访问主控件中的方法 (slot)
setCentralWidget(myWebView);
myWebView->setUrl( xxx ); //xxx是你的url或本地html路径
//. . .
class MainWindow : public QMainWindow
{
//. . .
public slots:
void CPlusPlusFunction(const QString& str) //这个函数是将被JavaScript调用的
{
myWebView->page()->mainFrame()->uateJavaScript( QObject::tr("jsFunction('Popup Dialog')") );
}
};
HTML文件内容如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>myjstest</title>
<script language="JavaScript" type="text/javascript">
function jsFunction(values) //this function will be called from C++ codes
{
alert(values);
}
function test()
{
mainWindowObject.CPlusPlusFunction( "calling C++ function from javaScript" );
}
</script>
</head>
<body>
<div id="dest"></div><form action="" method="post">
<input type="button" name="" value="myTest" onclick="test()" />
</form>
</body>
</html>
这种方法,使用Webkit作为浏览器,如果你的页面使用了ActiveX控件(比如google earth插件),则不能正常工作。
这种情况下,你需要放弃Webkit,在主程序中调用IE 控件(WebBrowser Control)作为浏览器。(但是这样也失去了跨平台的支持,因为IE只能在Wndosw上跑。)
这就是下面要介绍的解决方案2。
解决方案2:使用IE控件作为浏览器
myWebBrowser = new QAxWidget(this); //this 是main window widget, myWebBrowser 是它的成员变量
myWebBrowser->setControl(QString::fromUtf8("{8856F961-340A-11D0-A96B-00C04FD705A2}")); //设置IE控件
myWebBrowser->setObjectName(QString::fromUtf8("WebBrowser"));
myWebBrowser->setFocusPolicy(Qt::StrongFocus);
setCentralWidget(myWebBrowser);
myWebBrowser->dynamicCall("Navigate(const QString&)", xxx); //打开一个页面,xxx是你的url或本地html路径
//. . . 等页面加载好后,使用C++代码调用JS代码
IWebBrowser2 *webBrowser = 0;
myWebBrowser->queryInterface(IID_IWebBrowser2, (void **)&webBrowser);
if (webBrowser) {
CComPtr<IDispatch> spDisp = NULL;
webBrowser->get_Document(&spDisp);
myWebPage.SetDocument(spDisp); //注意,myWebPage的类型是CWebPage,关于它的定义,稍后有介绍
myWebPage.CallJScript("f2", "abc"); //调用页面中的f2函数,它有一个参数,这里填“abc”
webBrowser->Release();
}
//. . . CWebPage类的定义(粘贴的代码有点长,但是其实读起来很简单的)
头文件(代码片段):
class CWebPage
{
public:
CWebPage();
virtual ~CWebPage();
bool SetDocument(IDispatch* pDisp);
LPDISPATCH GetHtmlDocument() const;
const string GetLastError() const;
bool GetJScript(CComPtr<IDispatch>& spDisp);
bool GetJScripts(CComPtr<IHTMLElementCollection>& spColl);
bool CallJScript(const string strFunc,CComVariant* pVarResult = NULL);
bool CallJScript(const string strFunc,const string strArg1,CComVariant* pVarResult = NULL);
bool CallJScript(const string strFunc,const vector<string>& paramArray,CComVariant* pVarResult = NULL); //关键看这个函数
protected:
void ShowError(LPCSTR lpszText);
protected:
CComPtr<IHTMLDocument2> m_spDoc;
string m_strError;
};
CPP文件(代码片段):
bool CWebPage::SetDocument(IDispatch* pDisp)
{
CHECK_POINTER(pDisp);
m_spDoc = NULL;
CComPtr<IDispatch> spDisp = pDisp;
HRESULT hr = spDisp->QueryInterface(IID_IHTMLDocument2,(void**)&m_spDoc);
if(FAILED(hr))
{
ShowError("Failed to get HTML document COM object");
return false;
}
return true;
}
bool CWebPage::GetJScript(CComPtr<IDispatch>& spDisp)
{
CHECK_POINTER(m_spDoc);
if (NULL == m_spDoc)
{
return false;
}
HRESULT hr = m_spDoc->get_Script(&spDisp);
ATLASSERT(SUCCEEDED(hr));
return SUCCEEDED(hr);
}
bool CWebPage::GetJScripts(CComPtr<IHTMLElementCollection>& spColl)
{
CHECK_POINTER(m_spDoc);
HRESULT hr = m_spDoc->get_scripts(&spColl);
ATLASSERT(SUCCEEDED(hr));
return SUCCEEDED(hr);
}
bool CWebPage::CallJScript(const string strFunc,CComVariant* pVarResult)
{
vector<string> paramArray;
return CallJScript(strFunc,paramArray,pVarResult);
}
bool CWebPage::CallJScript(const string strFunc,const string strArg1,CComVariant* pVarResult)
{
vector<string> paramArray;
paramArray.push_back(strArg1);
return CallJScript(strFunc,paramArray,pVarResult);
}
bool CWebPage::CallJScript(const string strFunc, const vector<string>& paramArray,CComVariant* pVarResult)
{
CComPtr<IDispatch> spScript;
if(!GetJScript(spScript))
{
ShowError("Cannot GetScript");
return false;
}
CComBSTR bstrMember(strFunc.c_str());
DISPID dispid = NULL;
HRESULT hr = spScript->GetIDsOfNames(IID_NULL,&bstrMember,1,
LOCALE_SYSTEM_DEFAULT,&dispid);
if(FAILED(hr))
{
ShowError(GetSystemErrorMessage(hr).c_str());
return false;
}
const int arraySize = paramArray.size();
DISPPARAMS dispparams;
memset(&dispparams, 0, sizeof dispparams);
dispparams.cArgs = arraySize;
dispparams.rgvarg = new VARIANT[dispparams.cArgs];
for( int i = 0; i < arraySize; i++)
{
CComBSTR bstr = paramArray[arraySize - 1 - i].c_str();
bstr.CopyTo(&dispparams.rgvarg[i].bstrVal);
dispparams.rgvarg[i].vt = VT_BSTR;
}
dispparams.cNamedArgs = 0;
EXCEPINFO excepInfo;
memset(&excepInfo, 0, sizeof excepInfo);
CComVariant vaResult;
UINT nArgErr = (UINT)-1; // initialize to invalid arg
hr = spScript->Invoke(dispid,IID_NULL,0,
DISPATCH_METHOD,&dispparams,&vaResult,&excepInfo,&nArgErr);
delete [] dispparams.rgvarg;
if(FAILED(hr))
{
ShowError(GetSystemErrorMessage(hr).c_str());
return false;
}
if(pVarResult)
{
*pVarResult = vaResult;
}
return true;
}
测试的HTML页面
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>myjstest</title>
<script language="JavaScript" type="text/javascript">
function f1()
{
alert("void");
}
function f2(values)
{
alert(values);
}
</script>
</head>
<body>
</body>
</html>