這一章,我們來動手實踐VC調用JS函數。
我們動手寫一個HTML,其中包含這樣一段JS代碼:
//[html] <script type="text/javascript"> function Add(value1, value2) { return value1 + value2; } </script>
然后我們用WebBrowser加載這個HTML后,在VC中這樣來調用這個函數名為Add的JS函數:
//[Cpp] //別忘了#include <MsHTML.h> void CTestCallJsDlg::OnBnClickedOk() { //m_WebBrowser是一個WebBrowser的Activex控件對象。 CComQIPtr<IHTMLDocument2> spDoc = m_WebBrowser.get_Document(); CComDispatchDriver spScript; spDoc->get_Script(&spScript); CComVariant var1 = 10, var2 = 20, varRet; spScript.Invoke2(L"Add", &var1, &var2, &varRet); }
調試(Debug):
spScript.Invoke2的作用是調用JS函數中名為Add的函數,傳入兩個參數,用varRet接收返回值。
可以看到,Invoke2調用成功后,varRet得到了返回值30。
但這樣的話一次只能接受一個返回值。
如果要一次接受多個返回值的話,怎么辦呢?
我們可以讓JS返回一個JS中的Array數組或Object對象。
當JS函數return一個Array或一個Object對象時,VC這邊的varRet將接受到一個代表該對象的IDispatch接口。我們仍然用CComDispatchDriver來管理這個IDispatch。用上一篇文章介紹的CComDispatchDriver的四個方法:
GetProperty
GetPropertyByName
PutProperty
PutPropertyByName
來從這個Array或Object對象中取出我們要的數據。
實踐是檢驗真理的唯一標准,讓我們再來寫一個JS函數:
//[html] view plaincopy在CODE上查看代碼片派生到我的代碼片 <script type="text/javascript"> function Add(value1, value2) { var array = new Array(); array[0] = value1; array[1] = value2; array[2] = value1 + value2; return array; } </script>
然后在VC中這樣寫:
//[cpp] view plaincopy在CODE上查看代碼片派生到我的代碼片 void CTestCallJsOnBnClickedOk() { CComQIPtr<IHTMLDocument2> spDoc = m_WebBrowser.get_Document(); CComDispatchDriver spScript; spDoc->get_Script(&spScript); CComVariant var1 = 10, var2 = 20, varRet; spScript.Invoke2(L"Add", &var1, &var2, &varRet); CComDispatchDriver spArray = varRet.pdispVal; //獲取數組中元素個數,這個length在JS中是Array對象的屬性,相信大家很熟悉 CComVariant varArrayLen; spArray.GetPropertyByName(L"length", &varArrayLen); //獲取數組中第0,1,2個元素的值: CComVariant varValue[3]; spArray.GetPropertyByName(L"0", &varValue[0]); spArray.GetPropertyByName(L"1", &varValue[1]); spArray.GetPropertyByName(L"2", &varValue[2]); }
調試(Debug):
可以看到,10,20,30,這三個JS函數返回的值已經躺在我們的varValue[3]里了。
當然,如果不知道JS返回的Array對象里面有幾個元素,我們可以在VC這邊獲取它的length屬性,然后在一個循環中取出數組中的每個值。
如果我們的JS函數返回一個包含有多個屬性值的Object對象,VC這邊該如何接收呢?
讓我們再來寫一個JS函數:
//[html] view plaincopy在CODE上查看代碼片派生到我的代碼片 <script type="text/javascript"> function Add(value1, value2) { var data = new Object(); data.result = value1 + value2; data.str = "Hello,我是小明!"; return data; } </script>
然后在VC中我們這樣接收:
//[cpp] void CTestCallJsOnBnClickedOk() { CComQIPtr<IHTMLDocument2> spDoc = m_WebBrowser.get_Document(); CComDispatchDriver spScript; spDoc->get_Script(&spScript); CComVariant var1 = 10, var2 = 20, varRet; spScript.Invoke2(L"Add", &var1, &var2, &varRet); CComDispatchDriver spData = varRet.pdispVal; CComVariant varValue1, varValue2; spData.GetPropertyByName(L"result", &varValue1); spData.GetPropertyByName(L"str", &varValue2); }
調試(Debug):
我們從JS返回的Object對象里取出了它的兩個屬性,result和str,分別是一個整形數據和一個字符串。
這里JS代碼是我們自己寫的,在VC這邊當然事先知道這個JS函數返回的對象有result和str這兩個屬性。
如果JS代碼不是我們寫的,或者它的屬性是事先不能確定的,該怎么辦呢?答案是使用IDispatchEx接口來枚舉這個對象的相關信息(方法名、屬性名)。
這個現在暫時不講,在后續的文章中會講。
當然,JS不只可以返回Object對象,返回什么對象都可以,當返回一個對象而非基本數據類型(整形、浮點、字符串)時,VC這邊收到的返回值是一個IDispatch,然后我們需要調用GetPropertyByName方法從這個IDispatch代表的對象中取出它的屬性來。
這樣一來,VC調用JS函數,傳遞參數給JS和JS返回返回值給VC,大致就都會了。
對於CComVariant包裝的VARIANT這種智能型變量,不了解的可以到網上看下相關資料。《深入解析ATL》之類的書上均有介紹。
值得注意的是ATL提供的這些CCom開頭的智能包裝類,並不依賴於ATL的動態庫。因為我在VC項目中並沒有選擇鏈接ATL,程序調試運行時進程加載的模塊中也有沒有ATL100.dll之類的模塊載入。大家可以放心使用而不用擔心依賴上ATL。
VC調用JS函數沒問題了。那么JS函數如何調用VC呢?我們將在下一篇文章中慢慢道來。