VC與JavaScript交互(二) ———— 調用JS函數

這一章,我們來動手實踐VC調用JS函數。


我們動手寫一個HTML,其中包含這樣一段JS代碼:

[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. <script type="text/javascript">  
  2.     function Add(value1, value2) {  
  3.         return value1 + value2;  
  4.     }  
  5. </script>  

然後我們用WebBrowser加載這個HTML後,在VC中這樣來調用這個函數名爲Add的JS函數:

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //別忘了#include <MsHTML.h>  
  2. //m_WebBrowser是一個WebBrowser的Activex控件對象。  
  3. CComQIPtr<IHTMLDocument2> spDoc = m_WebBrowser.get_Document();  
  4. CComDispatchDriver spScript;  
  5. spDoc->get_Script(&spScript);  
  6.   
  7. CComVariant var1 = 10, var2 = 20, varRet;  
  8. spScript.Invoke2(L"Add", &var1, &var2, &varRet);  



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 plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. <script type="text/javascript">  
  2.     function Add(value1, value2) {  
  3.         var array = new Array();  
  4.         array[0] = value1;  
  5.         array[1] = value2;  
  6.         array[2] = value1 + value2;  
  7.         return array;  
  8.     }  
  9. </script>  

然後在VC中這樣寫:

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. CComQIPtr<IHTMLDocument2> spDoc = m_WebBrowser.get_Document();  
  2. CComDispatchDriver spScript;  
  3. spDoc->get_Script(&spScript);  
  4.   
  5. CComVariant var1 = 10, var2 = 20, varRet;  
  6. spScript.Invoke2(L"Add", &var1, &var2, &varRet);  
  7.       
  8. CComDispatchDriver spArray = varRet.pdispVal;  
  9. //獲取數組中元素個數,這個length在JS中是Array對象的屬性,相信大家很熟悉  
  10. CComVariant varArrayLen;  
  11. spArray.GetPropertyByName(L"length", &varArrayLen);  
  12. //獲取數組中第0,1,2個元素的值:  
  13. CComVariant varValue[3];  
  14. spArray.GetPropertyByName(L"0", &varValue[0]);  
  15. spArray.GetPropertyByName(L"1", &varValue[1]);  
  16. spArray.GetPropertyByName(L"2", &varValue[2]);  



可以看到,10,20,30,這三個JS函數返回的值已經躺在我們的varValue[3]裏了。

當然,如果不知道JS返回的Array對象裏面有幾個元素,我們可以在VC這邊獲取它的length屬性,然後在一個循環中取出數組中的每個值。

 

如果我們的JS函數返回一個包含有多個屬性值的Object對象,VC這邊該如何接收呢?

讓我們再來寫一個JS函數:

[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. <script type="text/javascript">  
  2.     function Add(value1, value2) {  
  3.         var data = new Object();  
  4.         data.result = value1 + value2;  
  5.         data.str = "Hello,我是小明!";  
  6.         return data;  
  7.     }  
  8. </script>  

然後在VC中我們這樣接收:

[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. CComQIPtr<IHTMLDocument2> spDoc = m_WebBrowser.get_Document();  
  2.     CComDispatchDriver spScript;  
  3.     spDoc->get_Script(&spScript);  
  4.   
  5.     CComVariant var1 = 10, var2 = 20, varRet;  
  6.     spScript.Invoke2(L"Add", &var1, &var2, &varRet);  
  7.       
  8.     CComDispatchDriver spData = varRet.pdispVal;  
  9.     CComVariant varValue1, varValue2;  
  10.     spData.GetPropertyByName(L"result", &varValue1);  
  11.     spData.GetPropertyByName(L"str", &varValue2);  



我們從JS返回的Object對象裏取出了它的兩個屬性,result和str,分別是一個整形數據和一個字符串。

這裏JS代碼是我們自己寫的,在VC這邊當然事先知道這個JS函數返回的對象有result和str這兩個屬性。

如果JS代碼不是我們寫的,或者它的屬性是事先不能確定的,該怎麼辦呢?答案是使用IDispatchEx接口來枚舉這個對象的相關信息(方法名、屬性名)。這個現在暫時不講,在後續的文章中會講。


當然,JS不只可以返回Object對象,返回什麼對象都可以,當返回一個對象而非基本數據類型(整形、浮點、字符串)時,VC這邊收到的返回值是一個IDispatch,然後我們需要調用GetPropertyByName方法從這個IDispatch代表的對象中取出它的屬性來。


JS中生成的對象和數組,傳遞給VC後,可以用CComDispatchDriver方便的讀取,但如果要在VC這邊生成一個對象或數組傳遞給JS該怎麼辦呢?

很多時候我們用VC調用JS函數的時候,需要傳遞很多參數給它,參數少還好辦,給JS函數多寫幾個形參就行了,可是參數很多,多達數十個數百個怎麼辦?傳遞對象或數組纔是好辦法。JS中的對象和數組傳到VC這邊後就變成了一個IDispatch*,那麼同理我們構造一個IDispatch*傳給JS不就行了嗎?說起來容易做起來麻煩,具體實現可以看下一章《VC與JavaScript交互(三) ———— JS調用C++》,但實在是太麻煩了。
用JSON就行了!VC想傳遞什麼給JS,只需把要傳遞的數據放到JSON裏,然後把JSON字符串扔給JS,接下來JS用JSON.stringify()就把這個JSON字符串變成JS中的數組或對象了。
如此一來,VC與JS交互只需傳遞字符串就行了,VC這邊生成JSON的話手工拼接一下字符串也很簡單,也可用一些庫來實現,如RapidJSON、jsoncpp、Boost.PropertyTree等等。


這樣一來,VC調用JS函數,傳遞參數給JS和JS返回返回值給VC,大致就都會了。

對於CComVariant包裝的VARIANT這種智能型變量,不瞭解的可以到網上看下相關資料。《深入解析ATL》之類的書上均有介紹。

 

值得注意的是ATL提供的這些CCom開頭的智能包裝類,並不依賴於ATL的動態庫。因爲我在VC項目中並沒有選擇鏈接ATL,程序調試運行時進程加載的模塊中也有沒有ATL100.dll之類的模塊載入。大家可以放心使用而不用擔心依賴上ATL。

 

VC調用JS函數沒問題了。那麼JS函數如何調用VC呢?我們將在下一篇文章中慢慢道來。

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