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技术是近几年发展起来的新技术,它对于软件
的性能扩展有重要的意义。从本文以上几节的介绍可以看出,在应用
系统中提供脚本语言的支持并不困难,甚至非常简单,因此,这种技术
有着广泛的发展前景,而且我们也已经看到越来越多的应用系统提供
了脚本语言的支持。
本文旨在对脚本技术作一个基本的介绍,希望文中所讲述的内容
能帮助读者在工作中用好这种技术。
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技术是近几年发展起来的新技术,它对于软件
的性能扩展有重要的意义。从本文以上几节的介绍可以看出,在应用
系统中提供脚本语言的支持并不困难,甚至非常简单,因此,这种技术
有着广泛的发展前景,而且我们也已经看到越来越多的应用系统提供
了脚本语言的支持。
本文旨在对脚本技术作一个基本的介绍,希望文中所讲述的内容
能帮助读者在工作中用好这种技术。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了