在C#中调用VBScript和JavaScript等脚本的实现
在C#中调用VBScript、JavaScript等脚本的实现
作者:郑佐
2004-04-26
以前在做工作流(workflow)项目的时候,里面有一项就是在用户制定流程定义时可以编写脚本来控制活动的跳转,而这些脚本定义后存在数据库中,当流程启动的时候,工作流引擎会控制活动执行顺序,串型的两个活动比较简单,但有的活动到下一个活动有条件判断,或者存在多个分支,简单的还好,只要在数据库表中加个字段就可以实现,复杂一点的就需要通过脚本实现了。当时经验不够,几天都没找到快速的解决办法,想自己写一个自定义脚本引擎没有把握,而且时间也不够,还是在网上找找看吧,花了一些时间,还是找到了一个自认为比较好的解决办法,写出来同大家分享。
下面通过两部分来说明实现以及应用。
一.使用MSScriptControl
到微软的网站上下载Windows Script Control,它是一个ActiveX(R) 控件,所以在.NET中使用我Interop了一下。下载安装完成后,新建一个C#的Windows应用程序项目,在解决方案资源管理器中选中引用节点,右键点击选择添加引用菜单,弹出添加引用对话框,单击浏览找到安装Windows Script Control的目录,选取msscript.ocx文件确定。那么在引用节点下会增加一个MSScriptControl组件,下面是他Interop后的所有对象。
ScriptControl 对支持 ActiveX(TM) Script 的宿主 Script 引擎提供简单接口。接下来我们对被转化成ScriptControlClass类的ScriptControl的属性和方法进行一些说明。
属性
AllowUI 属性:应用于 ScriptControl 本身或 Scirpt 引擎显示的用户界面元素,可读写。
CodeObject 属性:返回对象,该对象用于调用指定模块的公用成员。只读。
Error 属性:返回 Error 对象,其中包含所发生的最后一个错误的相关详细信息。只读。
Language 属性:设置或返回正在使用的 Script 语言名称。可读写。
Modules 属性:为 ScriptControl 对象返回模块集合。只读。
Procedures 属性:返回在指定模块中定义的过程集合。只读。
SitehWnd 属性:设置或返回窗口的 hWnd,通过执行 Script 代码,此窗口用于显示对话框和其他用户界面元素。可读写。
State 属性:设置或返回 ScriptControl 对象的模式。可读写。
Timeout 属性:设置或返回时间(毫秒),此时间后用户可选择中止 Script 代码的执行或允许代码继续执行。可读写。
UseSafeSubset 属性:设置或返回 Boolean 值,指明宿主应用程序是否有保密性要求。如果宿主应用程序需要安全控制,则 UseSafeSubset 为 True,否则为 False。可读写。
方法
AddCode 方法:向模块添加指定代码。可多次调用 AddCode 方法。
AddObject 方法:使主机对象模型对 Script 引擎可用。
Eval 方法:计算表达式并返回结果。
ExecuteStatement 方法:执行指定的语句。
Reset 方法:放弃所有已经添加到 ScriptControl 中的 Script 代码和对象。
Run 方法:运行指定过程。
事件
Error 事件:出现运行时错误时,发生此事件。
Timeout 事件:当超出了 Timeout 属性指定的时间且用户在结果对话框中选定了 End 时,发生此事件。
补充几点
AllowUI 属性如果设置为false,则显示对话框之类的语句不起作用,如在 VBScript 中MsgBox 语句,JavaScript中的alert等,并且如果执行的脚本超出TimeOut设置的毫秒数,也不会跳出超出时间提醒的对话框,反之则相反;重新设置 Language 属性会清空AddCode加载的代码;对于TimeOut属性,发生超时时,ScriptControl 检查对象的 AllowUI 属性,确定是否允许显示用户界面元素。
如果读者需要更详细的了解,可以查看MSDN文档。
为了使控件更容易使用,我用一个ScriptEngine类包装了一下,下面是完整代码:
using System;
using MSScriptControl;
using System.Text;
namespace ZZ
{
/// <summary>
/// 脚本类型
/// </summary>
public enum ScriptLanguage
{
/// <summary>
/// JScript脚本语言
/// </summary>
JScript,
/// <summary>
/// VBscript脚本语言
/// </summary>
VBscript,
/// <summary>
/// JavaScript脚本语言
/// </summary>
JavaScript
}
/// <summary>
/// 脚本运行错误代理
/// </summary>
public delegate void RunErrorHandler();
/// <summary>
/// 脚本运行超时代理
/// </summary>
public delegate void RunTimeoutHandler();
/// <summary>
/// ScriptEngine类
/// </summary>
public class ScriptEngine
{
private ScriptControl msc;
//定义脚本运行错误事件
public event RunErrorHandler RunError;
//定义脚本运行超时事件
public event RunTimeoutHandler RunTimeout;
/// <summary>
///构造函数
/// </summary>
public ScriptEngine():this(ScriptLanguage.VBscript)
{
}
/// <summary>
/// 构造函数
/// </summary>
/// <param name="language">脚本类型</param>
public ScriptEngine(ScriptLanguage language)
{
this.msc = new ScriptControlClass();
this.msc.UseSafeSubset = true;
this.msc.Language = language.ToString();
((DScriptControlSource_Event)this.msc).Error += new DScriptControlSource_ErrorEventHandler(ScriptEngine_Error);
((DScriptControlSource_Event)this.msc).Timeout += new DScriptControlSource_TimeoutEventHandler(ScriptEngine_Timeout);
}
/// <summary>
/// 运行Eval方法
/// </summary>
/// <param name="expression">表达式</param>
/// <param name="codeBody">函数体</param>
/// <returns>返回值object</returns>
public object Eval(string expression,string codeBody)
{
msc.AddCode(codeBody);
return msc.Eval(expression);
}
/// <summary>
/// 运行Eval方法
/// </summary>
/// <param name="language">脚本语言</param>
/// <param name="expression">表达式</param>
/// <param name="codeBody">函数体</param>
/// <returns>返回值object</returns>
public object Eval(ScriptLanguage language,string expression,string codeBody)
{
if(this.Language != language)
this.Language = language;
return Eval(expression,codeBody);
}
/// <summary>
/// 运行Run方法
/// </summary>
/// <param name="mainFunctionName">入口函数名称</param>
/// <param name="parameters">参数</param>
/// <param name="codeBody">函数体</param>
/// <returns>返回值object</returns>
public object Run(string mainFunctionName,object[] parameters,string codeBody)
{
this.msc.AddCode(codeBody);
return msc.Run(mainFunctionName,ref parameters);
}
/// <summary>
/// 运行Run方法
/// </summary>
/// <param name="language">脚本语言</param>
/// <param name="mainFunctionName">入口函数名称</param>
/// <param name="parameters">参数</param>
/// <param name="codeBody">函数体</param>
/// <returns>返回值object</returns>
public object Run(ScriptLanguage language,string mainFunctionName,object[] parameters,string codeBody)
{
if(this.Language != language)
this.Language = language;
return Run(mainFunctionName,parameters,codeBody);
}
/// <summary>
/// 放弃所有已经添加到 ScriptControl 中的 Script 代码和对象
/// </summary>
public void Reset()
{
this.msc.Reset();
}
/// <summary>
/// 获取或设置脚本语言
/// </summary>
public ScriptLanguage Language
{
get{return (ScriptLanguage)Enum.Parse(typeof(ScriptLanguage),this.msc.Language,false);}
set{this.msc.Language = value.ToString();}
}
/// <summary>
/// 获取或设置脚本执行时间,单位为毫秒
/// </summary>
public int Timeout
{
get{return this.msc.Timeout;}
set{this.msc.Timeout = value;}
}
/// <summary>
/// 设置是否显示用户界面元素
/// </summary>
public bool AllowUI
{
get{return this.msc.AllowUI;}
set{this.msc.AllowUI = value;}
}
/// <summary>
/// 宿主应用程序是否有保密性要求
/// </summary>
public bool UseSafeSubset
{
get{return this.msc.UseSafeSubset;}
set{this.msc.UseSafeSubset = true;}
}
/// <summary>
/// RunError事件激发
/// </summary>
private void OnError()
{
if(RunError!=null)
RunError();
}
/// <summary>
/// OnTimeout事件激发
/// </summary>
private void OnTimeout()
{
if(RunTimeout!=null)
RunTimeout();
}
private void ScriptEngine_Error()
{
OnError();
}
private void ScriptEngine_Timeout()
{
OnTimeout();
}
}
}
上面的包装定义了一个ScriptLanguage枚举,这样操作起来更方便一点。另外脚本引擎包括了Error事件和Timeout事件,根据实际使用情况可进行注册。
二.脚本引擎演示
我建了个窗体程序,测试包括脚本语言的选择,是否开启AllowUI属性,超时时间的设置,以及脚本引擎调用方法的选择。测试程序代码比较长,下面列出了主要部分:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
namespace ZZ
{
public class Form1 : System.Windows.Forms.Form
{
private ScriptEngine scriptEngine;
private System.Windows.Forms.CheckBox checkBoxAllowUI;
private System.Windows.Forms.TextBox textBoxResult;
private System.Windows.Forms.NumericUpDown numericUpDownTimeout;
private System.Windows.Forms.TextBox textBoxCodeBody;
private System.Windows.Forms.Button buttonRun;
private System.Windows.Forms.Button buttonCancel;
private System.Windows.Forms.ComboBox comboBoxScript;
private System.Windows.Forms.TextBox textBoxParams;
private System.Windows.Forms.RadioButton radioButtonEval;
private System.Windows.Forms.RadioButton radioButtonRun;
private System.Windows.Forms.TextBox textBoxMethodName;
private System.ComponentModel.Container components = null;
public Form1()
{
InitializeComponent();
this.comboBoxScript.SelectedIndex = 0;
this.scriptEngine = new ScriptEngine();
this.scriptEngine.UseSafeSubset = true;
this.scriptEngine.RunError += new RunErrorHandler(scriptEngine_RunError);
this.scriptEngine.RunTimeout += new RunTimeoutHandler(scriptEngine_RunTimeout);
}
protected override void Dispose( bool disposing )
{
if( disposing )
if (components != null)
components.Dispose();
base.Dispose( disposing );
}
#region Windows 窗体设计器生成的代码
private void InitializeComponent()
{
//省略
}
#endregion
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
//运行脚本
private void buttonRun_Click(object sender, System.EventArgs e)
{
this.scriptEngine.Reset();
this.scriptEngine.Language = (ScriptLanguage)Enum.Parse(typeof(ScriptLanguage),this.comboBoxScript.SelectedItem.ToString());
this.scriptEngine.Timeout = (int)this.numericUpDownTimeout.Value;
this.scriptEngine.AllowUI = this.checkBoxAllowUI.Checked;
if(this.radioButtonEval.Checked)//执行Eval方法
{
this.textBoxResult.Text = this.scriptEngine.Eval(this.textBoxMethodName.Text+"("+this.textBoxParams.Text+")",this.textBoxCodeBody.Text).ToString();
}
else//执行Run方法
{
string[] parameters = (string[])this.textBoxParams.Text.Split(',');
object [] paramArray = new object[parameters.Length];
for(int i = 0;i<parameters.Length;i++)
paramArray[i] = Int32.Parse(parameters[i]);
this.textBoxResult.Text = this.scriptEngine.Run(this.textBoxMethodName.Text,paramArray,this.textBoxCodeBody.Text).ToString();
}
}
//退出程序
private void buttonCancel_Click(object sender, System.EventArgs e)
{
this.Close();
}
//错误函数
private void scriptEngine_RunError()
{
MessageBox.Show("RunError执行脚本错误!");
}
private void scriptEngine_RunTimeout()
{
MessageBox.Show("RunTimeout执行脚本超时,引发错误!");
}
}
}
在文本框中写了一个JavaScript的函数。输入12,输出12000012。
如果把超时时间调整为1毫秒,那么执行该脚本就会跳出下面的超时提醒框,同时激发事件。
总结,上面演示了JavaScript脚本,如果有兴趣读者可以写一些VBsript函数进行测试,脚本语言也只列出了三种,看了帮助,他还支持其他一些脚本,如果需要可以添加。另外,因为是调用Com,有些返回值是obejct类型的,需要进行转换。在CSDN的技术论坛C#板块下时常有朋友问这方面的问题,对于碰到这类问题的朋友,希望通过这篇文章能获得一些你需要的帮助,很高兴能和搞.net的朋友进行交流,我的邮件地址zhzuocn@163.com。
引用 :http://blog.csdn.net/zhzuo/article/details/22031
资料: http://www.cnblogs.com/hailexuexi/archive/2011/02/15/1955166.html
在C#中调用VBScript和JavaScript等脚本的实现的更多相关文章
- Shell中 调用/引用/包含 另外的脚本文件的两种方法
脚本 first (测试示例1) #!/bin/bash echo 'your are in first file' 问)在当前脚本文件中调用另外一个脚本文件? 方法一: 使用 source 脚本 s ...
- 如何在java程序中调用linux命令或者shell脚本
转自:http://blog.sina.com.cn/s/blog_6433391301019bpn.html 在java程序中如何调用linux的命令?如何调用shell脚本呢? 这里不得不提到ja ...
- 小程序:如何在wxml页面中调用JavaScript函数
早上过来遇到一个这样的bug: 在计算百分比的时候没有保留小数点后2位,从而导致一些无法整除的结果显示太长 一开始,我以为这是一个很普通的bug,既然wxml在页面{{}}内支持简单的运算,我想也应该 ...
- django系列5.4--ORM中执行原生SQL语句, Python脚本中调用django环境
ORM执行原生sql语句 在模型查询API不够用的情况下,我们还可以使用原始的SQL语句进行查询. Django 提供两种方法使用原始SQL进行查询:一种是使用raw()方法,进行原始SQL查询并返回 ...
- django系列5.5--分组查询,聚合查询,F查询,Q查询,脚本中调用django环境
一.聚合查询 aggregate(*args, **args) 先引入需要的包,再使用聚合查询 #计算所有图书的平均价格 from django.db.models import Avg Book.o ...
- Java代码调用服务器上的Shell脚本
Java代码调用服务器上的Shell脚本 这里主要是因为我们报表平台有用到用户手工录入的数据作为结果数据且需要纳入saiku去展示 如我们所知,saiku不会自动刷新,所以需要在数据更新接口中调用服务 ...
- C# winForm中调用javascript文件中的方法
目前有很多的SNS社区或类SNS的网站,例如开心.51.校内等,但是发现大多数社区在邀请好友的时候都没有提供对QQ邮箱或者QQ空间好友列表获取的功能,不过似乎海内支持,但是网上相关QQ的文章还不是很多 ...
- MSScriptControl详解(可实现在C#等语言中调用JAVASCRIPT代码)
ScriptControl接口 属性名称 类型 备注 AllowUI BOOL 检测是否允许运行用户的接口元素.如果为False,则诸如消息框之类的界面元素不可见. CodeObject Object ...
- asp.net中调用javascript自定义函数的方法(包括引入JavaScript文件)总结
通常javascript代码可以与HTML标签一起直接放在前 端页面中,但如果JS代码多的话一方面不利于维护,另一方面也对搜索引擎不友好,因为页面因此而变得臃肿:所以一般有良好开发习惯的程序员都会把 ...
随机推荐
- Linux Shell脚本编程的注意事项
Linux下(Shell脚本 http://www.jbxue.com/jb/shell/)编程的一些注意事项,如编程风格.命名风格等. 一.常用技巧 ssh user@server bash < ...
- mysql实体关系(mysql学习五)
实体关系 表设计 1:1 两个实体表内,存在相同的主键字段 如果记录的主键值等于另一个关系表内记录的主键值,则两条记录的对应为一一对应 优化上称为垂直分割 1:n 一个实体对应多个其他实体(一个班级 ...
- Yii中使用PHPexcel获取excel中数据
1.view中代码如下: <form name="frmBatchSettle" id="" action="" method=&qu ...
- PHP 只有登陆后才能浏览的简单实现
1.============================================================= 在你不想让别人直接进入的网页开头加一段代码: session_start ...
- devexpress 数据导入(gridcontrol 导出 csv)
// 1.gridcontrol 导出 csv: DataTable dtbNew = new DataTable(); dtbNew.Columns.Add().GetType()); dtbNew ...
- 网页绘制图表 Google Charts with JavaScript #1....好强、好简单啊!
此为文章备份,原文出处(我的网站) 网页绘制图表 Google Charts with JavaScript....好强.好简单啊!#1 http://www.dotblogs.com.tw/mis2 ...
- ES6 入门系列 - 函数的扩展
1函数参数的默认值 基本用法 在ES6之前,不能直接为函数的参数指定默认值,只能采用变通的方法. function log(x, y) { y = y || 'World'; console.log( ...
- android bluetooth UUID蓝牙查询表
ServiceDiscoveryServerServiceClassID_UUID = '{00001000-0000-1000-8000-00805F9B34FB}' BrowseGroupDesc ...
- mac media server
近日在mac osx基于开源组件nginx-rtmp-module架设了一台默认的media server,以下是过程笔记 下载https://github.com/arut/nginx-rtmp-m ...
- python生成带参数二维码
#coding:utf8 import urllib2 import urllib import json import string import random class WebChat(obje ...