ActiveX異步回調JavaScript

ActiveX異步回調JavaScriptjavascript

      開發環境:VC6.0html

      背景知識:COM/ActiveX/JavaScript/MFC/Threadjava

 

      想必用過Ajax的童鞋們都知道xmlhttp這個東西吧,經過設定onreadystatechange屬性,咱們就能夠指定他狀態改變的回調函數,當狀態改變時,ActiveX控件就會調用咱們經過onreadystatechange屬性制定的回調函數。從而就出現了Ajax給咱們帶來的精彩。關於Ajax的技術咱們這裏不作討論,咱們的目的就是實現像xmlhttp這樣具備異步回調JavaScript功能的ocx控件來。web

 

      Let’s go!數組

 

1.      創建MFC ActiveX Control(方法略)瀏覽器

2.      ClassWizard中添加屬性callbackfunction屬性,併爲該屬性生成getset方法。咱們將在ActiveX控件中開啓線程,線程執行完後將調用經過該屬性執行的JavaScript函數。在該實例中,經過callbackfunction屬性指定的JavaScript函數必須是返回值是void的,而且含有一個short類型的參數的函數。安全

3.      咱們須要一個方法來觸發回調函數,添加方法Invoke包含一個short類型的參數param。在這個函數裏將開啓一個線程進行運算,而後返回計算結果。並把結果以回調函數的形式調用JavaScript的函數。session

4.      Invoke方法中開啓線程。進行計算。線程同步的方法採用PostMessage自定義消息。這個很重要,不然的話,咱們在線程中操做界面控件是不正確的。(我就是忘記了進行線程同步纔多走了好多彎路)異步

#define WM_THREADFIREEVENT WM_USER+101ide

void f(void * r)

{

      CThirdCtrl* p = (CThirdCtrl*)r;

      Sleep(5000);

      p->m_param +=10;

       PostMessage(p->m_hWnd,WM_THREADFIREEVENT,(WPARAM)NULL,(LPARAM)NULL);

      return;

}

 

void CThirdCtrl::invoke(short param)

{

      m_param = param;

      _beginthread(f, 0, (void*)(this));

}

 

5.      添加THREADFIREEVENT消息的消息映射函數:

ON_MESSAGE(WM_THREADFIREEVENT,OnFireEventForThread)

 

      6.   實現函數OnFireEventForThread

LRESULT CThirdCtrl::OnFireEventForThread(WPARAM wParam, LPARAM lParam)

{

   //FireLengthyProcessDone();

      InvokeScript ();

      return TRUE;

}

7.  在實現InvokeScript前,先說一個重要的東西,就是OnSetClientSite這是一個CThirdCtrl的父類ColeControl的一個虛方法。咱們須要重寫他來得到IWebBrowser2指針,有了IWebBrowser2咱們就能夠隨心所欲了。比方說得到document對象,得到html中的elements,設定他們的屬性,調用方法。也能夠執行頁面中的JavaScript函數。

     爲得到頂層IWebBrowser2引用,從客戶站點獲取IServiceProvider接口而且執行一個QueryService 操做獲取IID_IServiceProvider服務:SID_STopLevelBrowser (這在Shlguid.h中定義);對第二個IServiceProvider,執行一個QueryService獲取IID_IWebBrowser2服務:SID_SWebBrowserApp.

     上代碼:

void CThirdCtrl::OnSetClientSite()

{

     

     IOleClientSite*  pClientSite  =  GetClientSite(); 

     

     HRESULT  hr  =  S_OK; 

     IServiceProvider  *isp,  *isp2  =  NULL;//用於導航DHTML對象層次,做用就是提供服務 

     

     if  (!pClientSite) 

     

      if(browser!=NULL)

      {

             browser->Release();

             browser = NULL;

      }

      return;//  !S_OK; 

     

     else 

     

      hr  =  pClientSite->QueryInterface(IID_IServiceProvider,  reinterpret_cast<void  **>(&isp)); 

      if  (FAILED(hr))   

      

             hr  =  S_OK; 

             goto  cleanup; 

      

      

      hr  =  isp->QueryService(SID_STopLevelBrowser,  IID_IServiceProvider,  reinterpret_cast<void  **>(&isp2)); 

      if  (FAILED(hr)) 

      

             hr  =  S_OK; 

             goto  cleanup; 

      

      

      //得到瀏覽器 

      hr  =  isp->QueryService(SID_SWebBrowserApp,  IID_IWebBrowser2,  reinterpret_cast<void  **>(&browser));

      if  (FAILED(hr))   

      

             hr  =  S_OK; 

             goto  cleanup; 

      

      

cleanup: 

      //  Free  resources. 

      if(isp!=NULL)

      {

             isp->Release();

             isp = NULL;

      }

      if(isp2!=NULL)

      {

             isp2->Release();

             isp2 = NULL;

      }

      return;//  hr; 

     

     

     return;//  hr;

     

}

             一樣的道理,若是咱們是ATL作的ActiveX,則須要重寫

STDMETHODIMP  CThirdCtrl::SetClientSite()

這個方法。

8.   下面就是最關鍵的InvokeScript函數的實現,咱們在這裏使用上面獲取到的IWebBrowser2指針來獲取document對象,而後獲取Idispatch接口的script對象,而後調用Idispatch接口的Invoke方法。就能夠調用JavaScript了。Idispatch接口真是強大啊。

廢話少說,上代碼:

void  CThirdCtrl::InvokeScript() 

if(!browser) 

       if(browser!=NULL)

       {

              browser->Release();

              browser = NULL;

       }

       return; 

CComPtr<IHTMLDocument2> m_spDoc; 

HRESULT hr = browser->get_Document((IDispatch**)&m_spDoc);   

if(FAILED(hr)) 

       throw(""); 

CComPtr<IDispatch> pScript; 

hr = m_spDoc->get_Script(&pScript); 

if(FAILED(hr)) 

       throw(""); 

CComBSTR  bstrMember(m_callbackfunction);   

DISPID  dispid; 

hr=pScript->GetIDsOfNames(IID_NULL,&bstrMember,1,LOCALE_SYSTEM_DEFAULT,&dispid); 

//  設置函數參數 

DISPPARAMS  dispparams; 

memset(&dispparams,0,sizeof(dispparams)); 

dispparams.cArgs = 1;//表示參數的計數。 

dispparams.rgvarg = new VARIANT[dispparams.cArgs];//表示對參數數組的引用。 

for(int i = 0; i < 1; i++) 

       //CComBSTR bstr = "111";  //  back  reading 

       //bstr.CopyTo(&dispparams.rgvarg[i].bstrVal); 

       dispparams.rgvarg[i].iVal = m_param;

       dispparams.rgvarg[i].vt = VT_I2; 

dispparams.cNamedArgs =0;//表示命名參數的計數。 

EXCEPINFO excepInfo; 

memset(&excepInfo,0,sizeof(excepInfo)); 

CComVariant vaResult; 

UINT nArgErr = (UINT)-1;  //  initialize  to  invalid  arg 

hr = pScript->Invoke(dispid, IID_NULL,0,DISPATCH_METHOD,&dispparams,&vaResult,&excepInfo,&nArgErr); 

這樣,ActiveX控件就完成了。

9.      編寫html頁面代碼。打開Microsoft ActiveX Control Pad,插入控件。而後編寫JavaScript代碼。

<HTML>

<HEAD>

<TITLE>New Page</TITLE>

</HEAD>

<BODY>

   <SCRIPT LANGUAGE="JavaScript" >

function invoke()

{

       Third1.callbackfunction = "callback";

       Third1.invoke(2);

       alert("begin invoke");

}

function callback(param)

{

       alert(param);

}

 

   </SCRIPT>

<OBJECT ID="Third1" WIDTH=100 HEIGHT=51

 CLASSID="CLSID:E9D38528-0F4E-468B-858D-69905F16942F">

   <PARAM NAME="_Version" VALUE="65536">

   <PARAM NAME="_ExtentX" VALUE="2646">

   <PARAM NAME="_ExtentY" VALUE="1323">

   <PARAM NAME="_StockProps" VALUE="0">

</OBJECT>

<input type="button" value="test" onclick="invoke();" />

</BODY>

</HTML>

10.  測試:打開瀏覽器,打開test.html頁面。點擊「test「按鈕,將會先顯示對話框begin invoke,而後過5秒鐘再顯示對話框12

11.  調試方法:咱們能夠直接調試瀏覽器。瀏覽器加載了控件,而後咱們調用控件的方法,這時會自動觸發咱們在工程中設置的斷點。在

project---settings---debug---executable for debug sessions設置瀏覽器的exe文件的路徑。我用的世界之窗瀏覽器。因此值設置爲:C:\Program Files\TheWorld\TheWorld.exe

若是你用IE瀏覽器,可設置爲:C:\Program Files\Internet Explorer\iexplore.exe

 

說明:

1.   上述控件與xmlhttp不一樣的地方是callbackfunction我傳的是一個字符串,而xmlhttp傳的是一個JavaScript的函數指針。

2.   COM中的線程模型不在本文討論範圍以內。還有瀏覽器安全問題和打包CAB的問題也不在本文討論範圍以內。

參考:

http://vcfaq.mvps.org/com/1.htm

http://vcfaq.mvps.org/com/11.htm

http://support.microsoft.com/kb/q157437/

相關文章
相關標籤/搜索