ActiveX Scripting技術(三)

ActiveX Scripting技術(三)(接上期)然後我們看看類CScriptHo
st中接口IActiveS criptSite的成員函數GetItemInfo的實現,因爲引
擎調用GetItemInfo函數獲取其名字空間中名字項的信息,所以我們要
在此函數中把應用系統的對象暴露給引擎和腳本,代碼如下:
    STDMETHODIMP CScriptHost::GetItemInfo(LPCOLESTR pstrName
,DWORD dwReturnM ask,IUnknown **ppiunkItem, ITypeInfo **ppti
)
    {
    HRESULT hr = S_OK;
    // initialize the sent-in pointers
    if(dwReturnMask & SCRIPTINFO_ITYPEINFO)
    {
    if(ppti == NULL)
    return E_INVALIDARG;
    *ppti = NULL;
    }
    if(dwReturnMask & SCRIPTINFO_IUNKNOWN)
    {
    if(ppiunkItem == NULL)
    return E_INVALIDARG;
    *ppiunkItem = NULL;
    }
    if(!_wcsicmp(m_pNamedItem, pstrName))
    {
    if(dwReturnMask & SCRIPTINFO_IUNKNOWN)
    {
    // give out the object's IUnknown pointer
    *ppiunkItem = m_lpUnkCtrl;
    static_cast(*ppiunkItem)- AddRef();
    }
    if(dwReturnMask & SCRIPTINFO_ITYPEINFO)
    {
    IProvideClassInfo* pClsInfo = NULL;
    hr = m_lpUnkCtrl- QueryInterface(IID_IProvideClassInfo,
(void**)&pClsInf o);
    if(pClsInfo != NULL)
    {
    hr = pClsInfo- GetClassInfo(ppti);
    pClsInfo- Release();
    }
    }
    }
    return hr;
    }
    函數GetItemInfo首先對輸出參數ppiunkItem和ppti進行有效性
檢查,然後判斷是否輸入的名字與應用支持的受控對象的名字一致,如
果一致的話,則根據參數dwReturnMask 所指示的標誌,把對象的IUnkn
own接口或者對象的類型信息通過輸出參數傳遞給引擎,供引擎解釋執
行腳本代碼使用。
    我們再看類CScriptHost中接口IActiveScriptSiteWindow的成員
函數GetWindow的實現。函數比較簡單,只是把應用系統的窗口句柄通
過輸出參數傳遞給引擎,代碼如下:
    HRESULT CScriptHost::GetWindow(HWND *phwnd)
    {
    if (m_Wnd != NULL) {
    *phwnd = m_Wnd;
    return S_OK;
    } else
    return E_FAIL;
    }
    類CScriptHost的其他成員函數都比較簡單,有的接口成員函數可
以不實現,僅僅返回S_OK或者E_NOTIMPL即可,其代碼不再一一列舉。
    CScriptHost提供了應用系統爲支持腳本代碼運行所做的基本工
作,CScriptHost爲引擎提供了應用系統的必要信息。CScriptHost類
是一個通用的類,如果應用系統只有一個Automation對象暴露給腳本
代碼,則可以用CScriptHost類快速實現對腳本代碼的支持。如果應用
系統有多個Automation對象要暴露給腳本代碼,則需要對上面介紹的C
ScriptHo st類作些修改,使其支持多個名字項的處理。
    四、ActiveX Scripting實例
    在這一節,我們通過一個實例來說明如何利用上節提供的CScript
Host類爲應用程序加上腳本特性。例程序很簡單,只是一個基於對話
框的應用,但對話框中有一個日曆控制,這是Microsoft提供的ActiveX
控制,它本身也是一個Automation對象,我們將在腳本代碼中對該日曆
對象進行控制,並且在腳本代碼中響應日曆控制的一些事件。圖3是例
程序的運行界面圖。
圖3 例程序運行界面圖
    創建例程序的過程並不複雜,利用Microsoft Visual C++ 5.0(或
6.0)提供的AppWiz ard和ClassWizard可以很快創建工程,並添加各項
功能,下面是其操作過程。
    (1)首先我們創建一個MFC工程,因爲例程序比較簡單,所以我們選
擇了基於對話框的應用類型。工程名爲Script,對話框類名爲CScript
Dlg。
    (2)然後我們在對話框資源模板中添加日曆控制,打開對話框模板
,用右鍵單擊,從菜單中選擇"Insert ActiveX Control"命令,選擇Cal
endar Control,然後調整大小合適即可,並設置控制的ID爲IDC_CONTR
OL1。
    (3)在對話框模板中添加兩個按鈕"Load Script"和"Run Script"
放在適當的位置上。
    (4)把上一節完成的文件ScriptHost.h和ScriptHost.cpp加入到
工程中。
    (5)在類CScriptDlg中加入數據成員m_pScHost,其類型爲CScript
Host *。
    (6)用ClassWizard生成按鈕"Load Script"的消息控制函數,編寫
代碼如下。
    void CScriptDlg::OnLoadscript()
    {
    CFileDialog dlg(TRUE, "*.txt","*.txt",OFN_HIDEREADONLY |
    OFN_OVERWRITEPROMPT,"Text files (*.txt)");
    if(dlg.DoModal()==IDOK)
    {
    CString strPath;
    strPath = dlg.GetPathName();
    if (strPath.IsEmpty())
    return;
    if (m_pScHost != NULL)
    m_pScHost-m_ps-Close();
    CWnd *pCalander = GetDlgItem(IDC_CONTROL1);
    m_pUnknownCtrl = pCalander- GetControlUnknown();
    m_pScHost = new CScriptHost(m_pUnknownCtrl, L"control",
m_hWnd);
    HRESULT hr = m_pScHost-CreateScriptEngine();
    hr = m_pScHost- ParseFile(strPath,L"control");
    GetDlgItem(IDC_RUNSCRIPT)-EnableWindow(TRUE);
    GetDlgItem(IDC_RUNSCRIPT)- SetWindowText("Run Script");
    return;
    }
    return;
    }
    在消息控制函數OnLoadscript中,首先打開標準文件對話框,待用
戶選中腳本文件後,取到文件名,放到變量strPath中,如果原先已經存
在引擎對象,則先關閉引擎對象。
    然後通過CWnd::GetControlUnknown函數取出日曆控制的IUnknow
n接口指針。完成了這些準備工作後,再構造一個CScriptHost對象,把
控制的IUnknown接口指針、控制名以及對話框的窗口句柄傳到CScrip
tHost對象中,然後調用其CreateScriptEngine成員函數創建腳本引擎
對象,創建完成後,再調用ParseFile成員函數裝入腳本文件。裝入腳
本之後, 設置"Run Script"按鈕使其接收運行腳本的命令。注意,在O
nLoadscript函數返回後,腳本引擎已經創建完成,腳本文件也已經裝
入到引擎中,但這時腳本代碼並沒有被運行。
    (7)用ClassWizard生成按鈕"Run Script"的消息控制函數,編寫
代碼如下。
    void CScriptDlg::OnRunscript()
    {
    if (m_pScHost != NULL) {
    SCRIPTSTATE ss;
    if (FAILED(m_pScHost- m_ps- GetScriptState(&ss)))
    return;
    if (ss == SCRIPTSTATE_CONNECTED) {m_pScHost- m_ps-SetScr
iptState(SCRIPTS TATE_DISCONNECTED);
    GetDlgItem(IDC_RUNSCRIPT)-SetWindowText("Run Script");
    } else {
    m_pScHost- m_ps- SetScriptState(SCRIPTSTATE_CONNECTED);
    GetDlgItem(IDC_RUNSCRIPT)- SetWindowText("Stop Script");
    }
    }
    }
    OnRunscript函數比較簡單,它調用引擎的IActiveScript接口的G
etScriptState成員函數獲取當前引擎的狀態,如果當前引擎中腳本正
在運行,則調用SetScriptState成員函數使引擎停止運行,引擎進入非
運行狀態,並設置"Run Script"按鈕的標題變爲"Run Scr ipt";如果
當前引擎中腳本不在運行,則調用SetScriptState成員函數使引擎進
入運行狀態,並設置"Run Script"按鈕的標題變爲"Stop Script"。
    (8)編譯並連接例程序。
    至此我們已經完成了例程序的創建工作,接下來我們再寫一個腳
本文件用來測試例程序是否能正確運行腳本文件。腳本文件中的代碼
分兩部分,一部分是全局的執行代碼,當引擎首次被啓動時,這部分代
碼就開始運行;另一部分是事件響應函數,當日歷控制產生事件時,腳
本代碼中的事件響應函數就會被執行。爲了測試例程序的正確性,我
們使用了下面的腳本代碼。
    ' Golobal code
    MSGBOX("Global!")
    '---------------------------------------------
    Sub control_DblClick()
    control.Nextyear
    MSGBOX("You have double-clicked!")
    End Sub
    '----------------------------------------------
    上述腳本代碼並不進行實際的操作,只是彈出一個消息框表明腳
本代碼獲得了控制。
    因爲我們在例程序的CScriptDlg::OnLoadscript函數中指定了應
用受控對象名字爲"control",所以在腳本代碼中的control_DblClick
函數即指響應"control"對象的"DblC lick"事件,在函數control_Dbl
Click中,調用了control對象方法Nextyear使當前日曆後翻一年。並
彈出消息框以示腳本代碼被執行了。
    程序執行過程中,對事件響應後的情況如圖4所示。圖4 例程序響
應雙擊事件後的運行結果
    如果我們單擊"Stop Script"按鈕停止腳本的執行,則再雙擊日曆
控制,腳本代碼中的事件響應函數不會被執行;如果用戶再單擊"Run S
cript"按鈕,則事件響應函數會再次被執行。如果用戶希望執行其他
的腳本文件,則可以單擊"Load Script"按鈕,重新裝入腳本文件。從
這裏我們可以看出,應用程序對腳本引擎的控制是非常靈活的。讀者
可以試一試。
    五、結束語
    Active Scripting技術是近幾年發展起來的新技術,它對於軟件
的性能擴展有重要的意義。從本文以上幾節的介紹可以看出,在應用
系統中提供腳本語言的支持並不困難,甚至非常簡單,因此,這種技術
有着廣泛的發展前景,而且我們也已經看到越來越多的應用系統提供
了腳本語言的支持。
    本文旨在對腳本技術作一個基本的介紹,希望文中所講述的內容
能幫助讀者在工作中用好這種技術。
發佈了13 篇原創文章 · 獲贊 1 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章