如何在.NET中實現腳本引擎 (CodeDom篇)

 .NET 本身提供了強大的腳本引擎,可以直接使用.NET CLR的任何編程語言作爲腳本語言,如VB.NET、C#、JScript, J#等等。使用腳本引擎,我們可以動態生成任意表達式、或動態導入任意腳本文件,並在任意時候執行。 
        經實踐發現,我們可以使用至少兩種不同的方式在.NET中使用腳本引擎:VsaEngine和CodeDom。 
        其實,CodeDom不能算是真正的腳本引擎,它實際上是編譯器。但是我們完全可以利用CodeDom來模擬腳本引擎。 
        使用Emit方法也能達到動態生成可執行代碼的目的,而且Emit生成的代碼不需要編譯,因此速度更快。但是Emit插入的實際上是彙編代碼,不能算是腳本語言。 
        本文介紹如何以CodeDom方式來動態生成可執行代碼。

(Teeta無法發佈完整的文章,有興趣查看整篇文章,請到:http://ly4cn.cnblogs.com/archive/2005/11/03/267989.html

    1.     構造一個編譯器

  • 設置編譯參數 
    編譯參數需要在CompilerParameters設置: 

CompilerOptions用於設置編譯器命令行參數
IncludeDebugInformation用於指示是否在內存在生成Assembly
GenerateInMemory用於指示是否在內存在生成Assembly
GenerateExecutable用於指示生成的Assembly類型是exe還是dll
OutputAssembly用於指示生成的程序文件名(僅在GenerateInMemory爲false的情況)
ReferencedAssemblies用於添加引用Assembly

例如:

theParameters.ReferencedAssemblies.Add("System.dll"); 
  • 創建指定語言的編譯器 
    編譯需要由指定語言的CodeDomProvider生成。

這裏列舉一些.NET的CodeDomProvider:        

vb.net Microsoft.VisualBasic.VBCodeProvider
C#Microsoft.CSharp.CSharpCodeProvider
jscriptMicrosoft.JScript.JScriptCodeProvider
J#Microsoft.VJSharp.VJSharpCodeProvider

以C#爲例,要創建C#編譯器,代碼如下: 

ICodeCompiler compiler = new Microsoft.CSharp.CSharpCodeProvider().CreateCompiler(); 

下面是完整的創建編譯器的例子:

        ///  
        
/// 創建相應腳本語言的編譯器 
        
/// 
 
        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();             
        }
 
 
        
///  
        
/// 添加引用對象。 
        
///  
        
/// 引用的文件名
 
        public void AddReference(string __strAssemblyName) 
        

            theParameters.ReferencedAssemblies.Add(__strAssemblyName); 
        }
 


2.     編譯源代碼 

        編譯源代碼相當簡單,只需一條語句就搞定了:

CompilerResults compilerResults  = compiler.CompileAssemblyFromSource(this.theParameters, this.SourceText); 

執行後,可以從compilerResults取得以下內容: 

NativeCompilerReturnValue編譯結果,用於檢查是否成功
Errors編譯時產生的錯誤和警告信息
CompiledAssembly如果編譯成功,則返回編譯生成的Assembly

  
示例函數: 

        /// 
        
/// 編譯腳本。編譯前將清空以前的編譯信息。
        
/// CompilerInfo將包含編譯時產生的錯誤信息。
        
/// 
        
/// 成功時返回True。不成功爲False。

        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.     執行代碼 

使用Reflection機制就可以很方便的執行Assembly中的代碼。 
我們假設編譯時使用的腳本代碼 this.SourceText 內容如下:

namespace test 

    
public class script 
    

        
static public void Main() 
        

            MessageBox.Show(
"Hello"); 
        }
 
    }
 
}
 

則相應的執行代碼爲: 

scriptEngine.Invoke("test.script""Main"null);

Invoke函數內容:

        /// <summary>
        
/// 執行指定的腳本函數(Method)。
        
/// 如果指定的類或模塊名,以及函數(Method)、或參數不正確,將會產生VsaException/VshException例外。
        
/// </summary>
        
/// <param name="__strModule">類或模塊名</param>
        
/// <param name="__strMethod">要執行的函數(Method)名字</param>
        
/// <param name="__Arguments">參數(數組)</param>
        
/// <returns>返回執行的結果</returns>

        public object Invoke(string __strModule, string __strMethod, object[] __Arguments)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章