這一章,我們來動手實踐VC調用JS函數。
我們動手寫一個HTML,其中包含這樣一段JS代碼:
- <script type="text/javascript">
- function Add(value1, value2) {
- return value1 + value2;
- }
- </script>
然後我們用WebBrowser加載這個HTML後,在VC中這樣來調用這個函數名爲Add的JS函數:
- //別忘了#include <MsHTML.h>
- //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);
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函數:
- <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中這樣寫:
- 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]);
可以看到,10,20,30,這三個JS函數返回的值已經躺在我們的varValue[3]裏了。
當然,如果不知道JS返回的Array對象裏面有幾個元素,我們可以在VC這邊獲取它的length屬性,然後在一個循環中取出數組中的每個值。
如果我們的JS函數返回一個包含有多個屬性值的Object對象,VC這邊該如何接收呢?
讓我們再來寫一個JS函數:
- <script type="text/javascript">
- function Add(value1, value2) {
- var data = new Object();
- data.result = value1 + value2;
- data.str = "Hello,我是小明!";
- return data;
- }
- </script>
然後在VC中我們這樣接收:
- 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);
我們從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呢?我們將在下一篇文章中慢慢道來。