为应用程序添加脚本访问功能
2012-11-16 18:01
148 查看
首先,要有一个可以通过代码访问的应用程序,两种方式,一种是程序提供应用接口,要么是API,要么是COM接口,另一种,能够获得程序全部的源代码。
以上条件具备后,接下来,比较专业一点的方式,建立一个脚本编辑器程序,一般用一个单文档的应用就行,View类里放一个RichEditCtrl或者第三方的脚本控件,如Scintilla等,主要是为了编辑脚本方便以及提供一个美观的界面,让脚本程序员用着舒服。建立完编辑程序之后,在其中加入编译及运行操作按钮,如果能够加上调试按钮并实现的话,最好不过了。
这些架子搭完后,就要选择脚本引擎,一般我们不需要自己去写引擎,有几种优秀而且容易获得的引擎可供选择。比如微软的Active Scripting,谷歌开发的V8引擎等。V8引擎用于javascript脚本,Active Scripting分为VBScript和JScript,如果仅做javascript脚本,那么选择V8,如果要做VB脚本的话,那就选择微软。
这里选择Active Scripting。为建立好的脚本编辑器程序添加一个脚本操作器,这个脚本操作器就是一个Atl简单对象,可以实现加载脚本、运行脚本、停止脚本三种操作。这三种操作都是自己定义的,然后别的程序可以调用的,而且调用起来很方便。其中运行脚本这个操作就要使用到Active Scripting技术了。要实现运行脚本功能,得在脚本操作器的要继承的类中添加CObject类、IActiveScriptSite接口和IActiveScriptSiteWindow接口。这三项是最基本的,如果要实现调试功能的话,还要加一些其它的接口。添加CObject类是因为脚本操作器的MapNamedItems要设置为类本身,所以操作器要成为CObject的子类,ManNamedItems是一个CMapStringToOb类,它的项必须要设置为CObject类。后面的两个接口都是AS最基本的接口。IActiveScriptSite接口的GetItemInfo方法是要实现的,OnScriptError事件是可选的,为了调试方便,最好是实现,其它方法则不用实现,直接返回S_OK或E_NOTIMPL就行。在实现GetItemInfo时,需要返回SCRIPTINFO_ITYPEINFO类型的信息时,可以使用IDispatch的方法:this->GetTypeInfo(0,
NULL, ppti);需要返回SCRIPTINFO_IUNKNOWN类型的信息时,则用IDispatch本身:*ppiunkItem = (IDispatch*)this;然后还要实现IActiveScriptSiteWindow接口的GetWindow方法,把hWnd直接赋为NULL就行,然后返回S_OK,其它方法直接返回S_OK。
这些都做完后,可以为脚本操作器添加一些应用程序的接口,比如你要读取变量,就添加一个ReadTag的方法,这些都能直接写到脚本里。
真正要实现脚本操作器的三个方法了:加载脚本、运行脚本和停止脚本。加载脚本好说,建立一个字符串成员变量,在方法中传入一个文本文件名的参数,然后把这个文本文件内容,也就是脚本赋值给这个变量。运行脚本,在脚本操作器类中建立IActiveScript和IActiveScriptParse变量,这是脚本操作器是最重要的两个变量。用下面这条语句建立IActiveScript指针变量:hr = CoCreateInstance(CLSID_VBScript, NULL, CLSCTX_INPROC_SERVER,
IID_IActiveScript, (void **)&Axs);然后用下面这条语句查询出IActiveScriptParse指针变量:hr = Axs->QueryInterface(IID_IActiveScriptParse, (void **)&Axsp);之后就是下面几条语句:
hr = Axs->SetScriptSite(this); // 设置脚本操作范围
hr = Axsp->InitNew();// 初始化
mapNamedItems[_T("App")] = this;// 设置命名项,脚本中可以直接使用
// 将命名项添加到脚本操作范围中
hr = Axs->AddNamedItem(L"App", SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE | SCRIPTITEM_GLOBALMEMBERS);
这时,已经做好运行前的准备工作,下面要真正往引擎中加载脚本了:
CString scriptText; // 脚本字符串
EXCEPINFO ei; // 异常信息
HRESULT hr;
scriptText = Script;
BSTR ParseMe = scriptText.AllocSysString(); // 将脚本字符串转化为BSTR
Axsp->ParseScriptText(ParseMe, L"App", NULL, NULL, 0, 0, 0L, NULL, &ei); // 先过一遍
// 最后,启动脚本
hr = Axs->SetScriptState(SCRIPTSTATE_CONNECTED);
运行脚本到这里就可以了,如果中间发生了故障,则要在OnScriptError中写上:Axs->Close();以及你要把故障信息输出到什么地方。如要不写这些,有可能脚本操作器无法正常结束,导致整个应用程序无法结束。
那么结束脚本,无非就是把Axs和Axsp清理一下:Axsp->Release();Axs->Release(),要注意顺序。
脚本操作器写完了,如何调用呢?用Atl建立的脚本操作器,我们命名为CScriptOper,调用时可以这样:
CComObject<CScriptOper> *oper= NULL;
CComObject<CScriptOper>::CreateInstance(&oper);
engine->AddRef(); // 这条不写的话,程序不能退出,很奇怪
BSTR file = _com_util::ConvertStringToBSTR("C:\\test.vb");
oper->LoadScript(file);
oper->Run();
oper->Stop();
SysFreeString(file);
oper->Release();
oper=NULL;
这几行调用都不用怎么写注释,看得很明白。最后要注意的是内存泄漏,值得一提的是BSTR变量是最容易泄漏的,要确保所有的BSTR都要用SysFreeString函数来释放一下。
下面写一个可以读数的脚本:
dim startDate
dim startTime
dim endDate
dim endTime
dim data
startDate = DateSerial(2012, 11, 14)
startTime = TimeSerial(10, 44, 00)
endDate = DateSerial(2012, 11, 14)
endTime = TimeSerial(10, 45, 00)
data = App.ReadTag ("Tag1", startDate, startTime, endDate, endTime)
dim dataString
dim dataLength
dataLength = UBound(data, 1)-LBound(data, 1) + 1 '获得数据的长度
dataString = CStr(dataLength)
App.ShowMessage dataString
dataString = CStr(data(0))
App.ShowMessage dataString
dataString = CStr(data(1))
App.ShowMessage dataString
这段脚本也很明白,这里面最有意思的地方就是data这个变量最后成了数组,因为VBS里面所有的变量都是VARIANT类型的,当然数组也是一样的,如果ReadTag没有读上来数的话,那么UBound这些函数就会发生运行时错误。VBS中的数组和COM里面的安全数组是一致的,关于安全数组,则是另外一个值得讨论的话题了。
以上条件具备后,接下来,比较专业一点的方式,建立一个脚本编辑器程序,一般用一个单文档的应用就行,View类里放一个RichEditCtrl或者第三方的脚本控件,如Scintilla等,主要是为了编辑脚本方便以及提供一个美观的界面,让脚本程序员用着舒服。建立完编辑程序之后,在其中加入编译及运行操作按钮,如果能够加上调试按钮并实现的话,最好不过了。
这些架子搭完后,就要选择脚本引擎,一般我们不需要自己去写引擎,有几种优秀而且容易获得的引擎可供选择。比如微软的Active Scripting,谷歌开发的V8引擎等。V8引擎用于javascript脚本,Active Scripting分为VBScript和JScript,如果仅做javascript脚本,那么选择V8,如果要做VB脚本的话,那就选择微软。
这里选择Active Scripting。为建立好的脚本编辑器程序添加一个脚本操作器,这个脚本操作器就是一个Atl简单对象,可以实现加载脚本、运行脚本、停止脚本三种操作。这三种操作都是自己定义的,然后别的程序可以调用的,而且调用起来很方便。其中运行脚本这个操作就要使用到Active Scripting技术了。要实现运行脚本功能,得在脚本操作器的要继承的类中添加CObject类、IActiveScriptSite接口和IActiveScriptSiteWindow接口。这三项是最基本的,如果要实现调试功能的话,还要加一些其它的接口。添加CObject类是因为脚本操作器的MapNamedItems要设置为类本身,所以操作器要成为CObject的子类,ManNamedItems是一个CMapStringToOb类,它的项必须要设置为CObject类。后面的两个接口都是AS最基本的接口。IActiveScriptSite接口的GetItemInfo方法是要实现的,OnScriptError事件是可选的,为了调试方便,最好是实现,其它方法则不用实现,直接返回S_OK或E_NOTIMPL就行。在实现GetItemInfo时,需要返回SCRIPTINFO_ITYPEINFO类型的信息时,可以使用IDispatch的方法:this->GetTypeInfo(0,
NULL, ppti);需要返回SCRIPTINFO_IUNKNOWN类型的信息时,则用IDispatch本身:*ppiunkItem = (IDispatch*)this;然后还要实现IActiveScriptSiteWindow接口的GetWindow方法,把hWnd直接赋为NULL就行,然后返回S_OK,其它方法直接返回S_OK。
这些都做完后,可以为脚本操作器添加一些应用程序的接口,比如你要读取变量,就添加一个ReadTag的方法,这些都能直接写到脚本里。
真正要实现脚本操作器的三个方法了:加载脚本、运行脚本和停止脚本。加载脚本好说,建立一个字符串成员变量,在方法中传入一个文本文件名的参数,然后把这个文本文件内容,也就是脚本赋值给这个变量。运行脚本,在脚本操作器类中建立IActiveScript和IActiveScriptParse变量,这是脚本操作器是最重要的两个变量。用下面这条语句建立IActiveScript指针变量:hr = CoCreateInstance(CLSID_VBScript, NULL, CLSCTX_INPROC_SERVER,
IID_IActiveScript, (void **)&Axs);然后用下面这条语句查询出IActiveScriptParse指针变量:hr = Axs->QueryInterface(IID_IActiveScriptParse, (void **)&Axsp);之后就是下面几条语句:
hr = Axs->SetScriptSite(this); // 设置脚本操作范围
hr = Axsp->InitNew();// 初始化
mapNamedItems[_T("App")] = this;// 设置命名项,脚本中可以直接使用
// 将命名项添加到脚本操作范围中
hr = Axs->AddNamedItem(L"App", SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE | SCRIPTITEM_GLOBALMEMBERS);
这时,已经做好运行前的准备工作,下面要真正往引擎中加载脚本了:
CString scriptText; // 脚本字符串
EXCEPINFO ei; // 异常信息
HRESULT hr;
scriptText = Script;
BSTR ParseMe = scriptText.AllocSysString(); // 将脚本字符串转化为BSTR
Axsp->ParseScriptText(ParseMe, L"App", NULL, NULL, 0, 0, 0L, NULL, &ei); // 先过一遍
// 最后,启动脚本
hr = Axs->SetScriptState(SCRIPTSTATE_CONNECTED);
运行脚本到这里就可以了,如果中间发生了故障,则要在OnScriptError中写上:Axs->Close();以及你要把故障信息输出到什么地方。如要不写这些,有可能脚本操作器无法正常结束,导致整个应用程序无法结束。
那么结束脚本,无非就是把Axs和Axsp清理一下:Axsp->Release();Axs->Release(),要注意顺序。
脚本操作器写完了,如何调用呢?用Atl建立的脚本操作器,我们命名为CScriptOper,调用时可以这样:
CComObject<CScriptOper> *oper= NULL;
CComObject<CScriptOper>::CreateInstance(&oper);
engine->AddRef(); // 这条不写的话,程序不能退出,很奇怪
BSTR file = _com_util::ConvertStringToBSTR("C:\\test.vb");
oper->LoadScript(file);
oper->Run();
oper->Stop();
SysFreeString(file);
oper->Release();
oper=NULL;
这几行调用都不用怎么写注释,看得很明白。最后要注意的是内存泄漏,值得一提的是BSTR变量是最容易泄漏的,要确保所有的BSTR都要用SysFreeString函数来释放一下。
下面写一个可以读数的脚本:
dim startDate
dim startTime
dim endDate
dim endTime
dim data
startDate = DateSerial(2012, 11, 14)
startTime = TimeSerial(10, 44, 00)
endDate = DateSerial(2012, 11, 14)
endTime = TimeSerial(10, 45, 00)
data = App.ReadTag ("Tag1", startDate, startTime, endDate, endTime)
dim dataString
dim dataLength
dataLength = UBound(data, 1)-LBound(data, 1) + 1 '获得数据的长度
dataString = CStr(dataLength)
App.ShowMessage dataString
dataString = CStr(data(0))
App.ShowMessage dataString
dataString = CStr(data(1))
App.ShowMessage dataString
这段脚本也很明白,这里面最有意思的地方就是data这个变量最后成了数组,因为VBS里面所有的变量都是VARIANT类型的,当然数组也是一样的,如果ReadTag没有读上来数的话,那么UBound这些函数就会发生运行时错误。VBS中的数组和COM里面的安全数组是一致的,关于安全数组,则是另外一个值得讨论的话题了。
相关文章推荐
- ASP.NET 添加 AJAX 和客户端功能_第一篇_将客户端脚本添加到 ASP.NET Web 应用程序中
- Visual Studio 2010 开发与调试IronPython脚本 为你的ERP/MIS 应用程序添加脚本功能
- 在VC中为应用程序添加图形超链接功能
- 使用ExpressionAnalysis、LexicalAnalysis以及DynamicDataSet为项目添加脚本功能
- 脚本文件linux 添加支持 U 盘热拔插功能
- DHTMLX 前端框架 建立你的一个应用程序 教程(七)-- 添加筛选功能
- ASP.NET 添加 AJAX 和客户端功能_第二篇_2.1_ASP.NET 网页中的客户端脚本
- 创建ASP.NET Core MVC应用程序(5)-添加查询功能 & 新字段
- 构建Postfix+Mysql+Dovecot邮件系统,实现以Web页面访问的功能、添加SASL认证以及TLS加密传输 推荐
- 配置XenDesktop7.x "本地应用程序访问"功能
- [转]让用户通过宏和插件向您的 .NET 应用程序添加功能
- 为应用程序添加脚本支持
- 在VC中为应用程序添加图形超链接功能
- 为项目安装添加WEB调用本地应用程序功能
- 添加curl的openssl的功能,实现https的正确访问
- 使应用程序具有脚本支持功能
- 为应用程序添加脚本支持
- 图像处理控件ImageGear for .NET如何为应用程序添加添加DICOM功能(1)
- 为自己的应用程序添加脚本支持
- [ubuntu] ubuntu13.04 添加右键脚本功能