以前在做工作流(workflow)項目的時候,里面有一項就是在用戶制定流程定義時可以編寫腳本來控制活動的跳轉,而這些腳本定義后存在數(shù)據(jù)庫中,當流程啟動的時候,工作流引擎會控制活動執(zhí)行順序,串型的兩個活動比較簡單,但有的活動到下一個活動有條件判斷,或者存在多個分支,簡單的還好,只要在數(shù)據(jù)庫表中加個字段就可以實現(xiàn),復雜一點的就需要通過腳本實現(xiàn)了。當時經(jīng)驗不夠,幾天都沒找到快速的解決辦法,想自己寫一個自定義腳本引擎沒有把握,而且時間也不夠,還是在網(wǎng)上找找看吧,花了一些時間,還是找到了一個自認為比較好的解決辦法,寫出來同大家分享。
下面通過兩部分來說明實現(xiàn)以及應用。
一.使用MSScriptControl
到微軟的網(wǎng)站上下載Windows Script Control,它是一個ActiveX(R) 控件,所以在.NET中使用我Interop了一下。下載安裝完成后,新建一個C#的Windows應用程序項目,在解決方案資源管理器中選中引用節(jié)點,右鍵點擊選擇添加引用菜單,彈出添加引用對話框,單擊瀏覽找到安裝Windows Script Control的目錄,選取msscript.ocx文件確定。那么在引用節(jié)點下會增加一個MSScriptControl組件,下面是他Interop后的所有對象。
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>
///構造函數(shù)
/// </summary>
public ScriptEngine():this(ScriptLanguage.VBscript)
{
}
/// <summary>
/// 構造函數(shù)
/// </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">函數(shù)體</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">函數(shù)體</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">入口函數(shù)名稱</param>
/// <param name="parameters">參數(shù)</param>
/// <param name="codeBody">函數(shù)體</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">入口函數(shù)名稱</param>
/// <param name="parameters">參數(shù)</param>
/// <param name="codeBody">函數(shù)體</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>
/// 放棄所有已經(jīng)添加到 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>
/// 獲取或設置腳本執(zhí)行時間,單位為毫秒
/// </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事件激發(fā)
/// </summary>
private void OnError()
{
if(RunError!=null)
RunError();
}
/// <summary>
/// OnTimeout事件激發(fā)
/// </summary>
private void OnTimeout()
{
if(RunTimeout!=null)
RunTimeout();
}
private void ScriptEngine_Error()
{
OnError();
}
private void ScriptEngine_Timeout()
{
OnTimeout();
}
}
}
上面的包裝定義了一個ScriptLanguage枚舉,這樣操作起來更方便一點。另外腳本引擎包括了Error事件和Timeout事件,根據(jù)實際使用情況可進行注冊。
二.腳本引擎演示
我建了個窗體程序,測試包括腳本語言的選擇,是否開啟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)//執(zhí)行Eval方法
{
this.textBoxResult.Text = this.scriptEngine.Eval(this.textBoxMethodName.Text+"("+this.textBoxParams.Text+")",this.textBoxCodeBody.Text).ToString();
}
else//執(zhí)行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();
}
//錯誤函數(shù)
private void scriptEngine_RunError()
{
MessageBox.Show("RunError執(zhí)行腳本錯誤!");
}
private void scriptEngine_RunTimeout()
{
MessageBox.Show("RunTimeout執(zhí)行腳本超時,引發(fā)錯誤!");
}
}
}
在文本框中寫了一個JavaScript的函數(shù)。輸入12,輸出12000012。
如果把超時時間調整為1毫秒,那么執(zhí)行該腳本就會跳出下面的超時提醒框,同時激發(fā)事件。
總結,上面演示了JavaScript腳本,如果有興趣讀者可以寫一些VBsript函數(shù)進行測試,腳本語言也只列出了三種,看了幫助,他還支持其他一些腳本,如果需要可以添加。另外,因為是調用Com,有些返回值是obejct類型的,需要進行轉換。在CSDN的技術論壇C#板塊下時常有朋友問這方面的問題,對于碰到這類問題的朋友,希望通過這篇文章能獲得一些你需要的幫助,很高興能和搞.net的朋友進行交流,我的郵件地址zhzuocn@163.com