ActiveX Scripting技术(三)

来源:互联网 发布:品茗高支模计算软件 编辑:程序博客网 时间:2024/05/16 02:13
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技术是近几年发展起来的新技术,它对于软件
的性能扩展有重要的意义。从本文以上几节的介绍可以看出,在应用
系统中提供脚本语言的支持并不困难,甚至非常简单,因此,这种技术
有着广泛的发展前景,而且我们也已经看到越来越多的应用系统提供
了脚本语言的支持。
    本文旨在对脚本技术作一个基本的介绍,希望文中所讲述的内容
能帮助读者在工作中用好这种技术。
原创粉丝点击