ActiveX非同步回調JavaScript

來源:互聯網
上載者:User

 

著作權聲明

請尊重原創作品。轉載請保持文章完整性,並以超連結形式註明原始作者“tingsking18”和主要站台地址,方便其他朋友提問和指正。

ActiveX非同步回調JavaScript

開發環境:VC6.0。

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

想必用過Ajax的童鞋們都知道xmlhttp這個東西吧,通過設定onreadystatechange屬性,我們就可以指定他狀態改變的回呼函數,當狀態改變時,ActiveX控制項就會調用我們通過onreadystatechange屬性制定的回呼函數。從而就出現了Ajax給我們帶來的精彩。關於Ajax的技術我們這裡不做討論,我們的目的就是實現像xmlhttp這樣具有非同步回調JavaScript功能的ocx控制項來。

Let’s go!

1. 建立MFC ActiveX Control(方法略)

2. 在ClassWizard中添加屬性callbackfunction屬性,並為該屬性產生get和set方法。我們將在ActiveX控制項中開啟線程,線程執行完後將調用通過該屬性執行的JavaScript函數。在該執行個體中,通過callbackfunction屬性指定的JavaScript函數必須是傳回值是void的,並且含有一個short類型的參數的函數。

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

4. 在Invoke方法中開啟線程。進行計算。線程同步的方法採用PostMessage自訂訊息。這個很重要,否則的話,我們線上程中操作介面控制項是不正確的。(我就是忘記了進行線程同步才多走了好多彎路)

#define WM_THREADFIREEVENT WM_USER+101

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/

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.