NET 本身提供了強(qiáng)大的腳本引擎,可以直接使用.NET CLR的任何編程語言作為腳本語言,如VB.NET、C#、JScript, J#等等。使用腳本引擎,我們可以動(dòng)態(tài)生成任意表達(dá)式、或動(dòng)態(tài)導(dǎo)入任意腳本文件,并在任意時(shí)候執(zhí)行。
在網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站設(shè)計(jì)過程中,需要針對(duì)客戶的行業(yè)特點(diǎn)、產(chǎn)品特性、目標(biāo)受眾和市場(chǎng)情況進(jìn)行定位分析,以確定網(wǎng)站的風(fēng)格、色彩、版式、交互等方面的設(shè)計(jì)方向。創(chuàng)新互聯(lián)還需要根據(jù)客戶的需求進(jìn)行功能模塊的開發(fā)和設(shè)計(jì),包括內(nèi)容管理、前臺(tái)展示、用戶權(quán)限管理、數(shù)據(jù)統(tǒng)計(jì)和安全保護(hù)等功能。
經(jīng)實(shí)踐發(fā)現(xiàn),我們可以使用至少兩種不同的方式在.NET中使用腳本引擎:VsaEngine和CodeDom。
其實(shí),CodeDom不能算是真正的腳本引擎,它實(shí)際上是編譯器。但是我們完全可以利用CodeDom來模擬腳本引擎。
使用Emit方法也能達(dá)到動(dòng)態(tài)生成可執(zhí)行代碼的目的,而且Emit生成的代碼不需要編譯,因此速度更快。但是Emit插入的實(shí)際上是匯編代碼,不能算是腳本語言。
本文介紹如何以CodeDom方式來動(dòng)態(tài)生成可執(zhí)行代碼。
如何在.NET中實(shí)現(xiàn)腳本引擎 (CodeDom篇) 沐楓網(wǎng)志
1. 構(gòu)造一個(gè)編譯器
設(shè)置編譯參數(shù)
編譯參數(shù)需要在CompilerParameters設(shè)置:
CompilerOptions 用于設(shè)置編譯器命令行參數(shù)
IncludeDebugInformation 用于指示是否在內(nèi)存在生成Assembly
GenerateInMemory 用于指示是否在內(nèi)存在生成Assembly
GenerateExecutable 用于指示生成的Assembly類型是exe還是dll
OutputAssembly 用于指示生成的程序文件名(僅在GenerateInMemory為false的情況)
ReferencedAssemblies 用于添加引用Assembly
例如:
theParameters.ReferencedAssemblies.Add("System.dll");
創(chuàng)建指定語言的編譯器
編譯需要由指定語言的CodeDomProvider生成。
這里列舉一些.NET的CodeDomProvider:
vb.net Microsoft.VisualBasic.VBCodeProvider
C# Microsoft.CSharp.CSharpCodeProvider
jscript Microsoft.JScript.JScriptCodeProvider
J# Microsoft.VJSharp.VJSharpCodeProvider
以C#為例,要?jiǎng)?chuàng)建C#編譯器,代碼如下:
//.NET 1.1/1.0
ICodeCompiler compiler = new Microsoft.CSharp.CSharpCodeProvider().CreateCompiler();
//.NET 2.0
ICodeCompiler compiler = (ICodeCompiler) new Microsoft.CSharp.CSharpCodeProvider();
下面是完整的創(chuàng)建編譯器的例子:
/// summary
/// 創(chuàng)建相應(yīng)腳本語言的編譯器
/// /summary
private void createCompiler(string strLanguage, bool debugMode, string strAssemblyFileName)
{
this.theParameters = new CompilerParameters();
this.theParameters.OutputAssembly = System.IO.Path.Combine(System.IO.Path.GetTempPath(), strAssemblyFileName + ".dll");
this.theParameters.GenerateExecutable = false;
this.theParameters.GenerateInMemory = true;
if(debugMode)
{
this.theParameters.IncludeDebugInformation = true;
this.theParameters.CompilerOptions += "/define:TRACE=1 /define:DEBUG=1 ";
}
else
{
this.theParameters.IncludeDebugInformation = false;
this.theParameters.CompilerOptions += "/define:TRACE=1 ";
}
AddReference("System.dll");
AddReference("System.Data.dll");
AddReference("System.Xml.dll");
strLanguage = strLanguage.ToLower();
CodeDomProvider theProvider;
if("visualbasic" == strLanguage || "vb" == strLanguage)
{
theProvider = new Microsoft.VisualBasic.VBCodeProvider();
if(debugMode)
theParameters.CompilerOptions += "/debug:full /optimize- /optionexplicit+ /optionstrict+ /optioncompare:text /imports:Microsoft.VisualBasic,System,System.Collections,System.Diagnostics ";
else
theParameters.CompilerOptions += "/optimize /optionexplicit+ /optionstrict+ /optioncompare:text /imports:Microsoft.VisualBasic,System,System.Collections,System.Diagnostics ";
AddReference("Microsoft.VisualBasic.dll");
}
else if("jscript" == strLanguage || "js" == strLanguage)
{
theProvider = new Microsoft.JScript.JScriptCodeProvider();
AddReference("Microsoft.JScript.dll");
}
else if("csharp" == strLanguage || "cs" == strLanguage || "c#" == strLanguage)
{
theProvider = new Microsoft.CSharp.CSharpCodeProvider();
if(!debugMode)
theParameters.CompilerOptions += "/optimize ";
}
// else if("jsharp" == strLanguage || "vj" == strLanguage || "j#" == strLanguage)
// {
// theProvider = new Microsoft.VJSharp.VJSharpCodeProvider();
// if(!debugMode)
// theParameters.CompilerOptions += "/optimize ";
// }
else
throw new System.Exception("指定的腳本語言不被支持。");
this.theCompiler = theProvider.CreateCompiler();
}
/// summary
/// 添加引用對(duì)象。
/// /summary
/// param name="__strAssemblyName"引用的文件名/param
public void AddReference(string __strAssemblyName)
{
theParameters.ReferencedAssemblies.Add(__strAssemblyName);
}
注:
在.NET Framework 2.0中,由于CreateCompiler方法被標(biāo)記作廢。為避免產(chǎn)生編譯警告,可直接返回CodeDomProvider作為編譯器:
this.theCompiler = (ICodeCompiler)theProvider;
2. 編譯源代碼
編譯源代碼相當(dāng)簡(jiǎn)單,只需一條語句就搞定了:
CompilerResults compilerResults = compiler.CompileAssemblyFromSource(this.theParameters, this.SourceText);
執(zhí)行后,可以從compilerResults取得以下內(nèi)容:
NativeCompilerReturnValue 編譯結(jié)果,用于檢查是否成功
Errors 編譯時(shí)產(chǎn)生的錯(cuò)誤和警告信息
CompiledAssembly 如果編譯成功,則返回編譯生成的Assembly
示例函數(shù):
/// summary
/// 編譯腳本。編譯前將清空以前的編譯信息。
/// CompilerInfo將包含編譯時(shí)產(chǎn)生的錯(cuò)誤信息。
/// /summary
/// returns成功時(shí)返回True。不成功為False。/returns
public bool Compile()
{
this.theCompilerInfo = "";
this.isCompiled = false;
this.theCompiledAssembly = null;
this.theCompilerResults = this.theCompiler.CompileAssemblyFromSource(this.theParameters, this.SourceText);
if(this.theCompilerResults.NativeCompilerReturnValue == 0)
{
this.isCompiled = true;
this.theCompiledAssembly = this.theCompilerResults.CompiledAssembly;
}
System.Text.StringBuilder compilerInfo = new System.Text.StringBuilder();
foreach(CompilerError err in this.theCompilerResults.Errors)
{
compilerInfo.Append(err.ToString());
compilerInfo.Append("/r/n");
}
theCompilerInfo = compilerInfo.ToString();
return isCompiled;
}
3. 執(zhí)行代碼
使用Reflection機(jī)制就可以很方便的執(zhí)行Assembly中的代碼。
我們假設(shè)編譯時(shí)使用的腳本代碼 this.SourceText 內(nèi)容如下:
namespace test
{
public class script
{
static public void Main()
{
MessageBox.Show("Hello");
}
}
}
則相應(yīng)的執(zhí)行代碼為:
scriptEngine.Invoke("test.script", "Main", null);
Invoke函數(shù)內(nèi)容:
/// summary
/// 執(zhí)行指定的腳本函數(shù)(Method)。
/// 如果指定的類或模塊名,以及函數(shù)(Method)、或參數(shù)不正確,將會(huì)產(chǎn)生VsaException/VshException例外。
/// /summary
/// param name="__strModule"類或模塊名/param
/// param name="__strMethod"要執(zhí)行的函數(shù)(Method)名字/param
/// param name="__Arguments"參數(shù)(數(shù)組)/param
/// returns返回執(zhí)行的結(jié)果/returns
public object Invoke(string __strModule, string __strMethod, object[] __Arguments)
{
if(!this.IsCompiled || this.theCompiledAssembly == null)
throw new System.Exception("腳本還沒有成功編譯");
Type __ModuleType = this.theCompiledAssembly.GetType(__strModule);
if(null == __ModuleType)
throw new System.Exception(string.Format("指定的類或模塊 ({0}) 未定義。", __strModule));
MethodInfo __MethodInfo = __ModuleType.GetMethod(__strMethod);
if(null == __MethodInfo)
throw new System.Exception(string.Format("指定的方法 ({0}::{1}) 未定義。", __strModule, __strMethod));
try
{
return __MethodInfo.Invoke(null, __Arguments);
}
catch( TargetParameterCountException )
{
throw new System.Exception(string.Format("指定的方法 ({0}:{1}) 參數(shù)錯(cuò)誤。", __strModule, __strMethod));
}
catch(System.Exception e)
{
System.Diagnostics.Trace.WriteLine(string.Format("執(zhí)行({0}:{1})錯(cuò)誤: {2}", __strModule, __strMethod, e.ToString()));
return null;
}
}
總結(jié):
CodeDom可以很方便的隨時(shí)編譯源代碼,并動(dòng)態(tài)執(zhí)行。雖然作為腳本引擎,它沒有VsaEngine正規(guī)和方便,但作為一般應(yīng)用,也夠用了。并且結(jié)合Reflection機(jī)制,它的功能比VsaEngine更強(qiáng)大:它可以編譯任何提供CompilerProvider的CLR語言(目前.NET自帶的語言中都有)。
當(dāng)然,它也有一些缺點(diǎn):它生成的Assembly不能動(dòng)態(tài)卸載。這在一般情況下不成問題,因?yàn)橐粋€(gè)源代碼只需編譯一次,并載入執(zhí)行,并不需要?jiǎng)討B(tài)卸載。
假如你需要做腳本編輯器時(shí),就要考慮這個(gè)問題,因?yàn)橛锌赡芤粋€(gè)腳本會(huì)因?yàn)樾扌薷母亩煌5闹匦戮幾g,從而造成不停的產(chǎn)生新的Assembly,最后將導(dǎo)致內(nèi)存被大量占用。要解決這個(gè)問題,需要將編譯器加載到獨(dú)立的AppDomain中,通過卸載AppDomain達(dá)到卸載所需的Assembly的目的。
vbs是解釋執(zhí)行的,在網(wǎng)頁上運(yùn)行的一般用的是 IE的庫,或者是ASP的庫,直接改后綴那種用的是mscrpt 的庫,所以很多對(duì)象是不能通用的
VB.net 編寫的程序當(dāng)然可以XP上運(yùn)行,但是要用到.NET的庫,就是要裝相應(yīng)版本的net framework 才可以運(yùn)行!
既然引用了COM組件,就可以直接使用了,無需用CreateObject創(chuàng)建。
Dim ScriptControl As New MSScriptControl.ScriptControl
ScriptControl.Language = "JavaScript" '設(shè)置語言種類
ScriptControl.AddCode("function TestFunc(){return 'Hello world!'}") '添加腳本代碼
MsgBox(ScriptControl.Run("TestFunc")) '顯示一下那段腳本的返回值(這里你將看到Hello world!字符)
部分代碼如下:
在窗口及WebBrowser創(chuàng)建完以后,寫此代碼:
WebBrowser.OnNewWindow2:=IE1NewWindow2;
給主窗口創(chuàng)建一個(gè)方法:
procedure TBrowForm.IE1NewWindow2(Sender: TObject; var ppDisp: IDispatch;var Cancel: WordBool);
Var NewForm:TBrowForm;
begin
try
NewForm :=TPopBrowerForm.Create(application);
NewForm.WebBrowser.OnNewWindow2 :=IE1NewWindow2;//如果在窗口的Oncreate事件
//有此代碼,此處就可以省略
ppDisp := NewForm.WebBrowser.Application;
NewForm.Show;
Cancel:=false;
except
end;
end;
”VBS腳本“可以用任何純
文本編輯
工具編寫,包括系統(tǒng)自帶的”記事本“。VB.NET當(dāng)然也可以。
如果你是需要語法提示之類的,VB.NET可以提供絕大部分的語法提示。畢竟
VBS
是
VB
的子集。
非也,乃微軟新一代面向?qū)ο罂梢暬幊陶Z言。另外,vb.net和vb6.0及以前版本都有很大差別。屬.net平臺(tái)。.net framework現(xiàn)在最新的版本是3.5,建議你學(xué)習(xí)2.0版本即可,有機(jī)會(huì)再看看3.5。