C#で作ったexe上で、C#のソースコードをコンパイル・実行できる。ライブラリ不要!
※何でもできるので、わりと危険。
きーわーど: 動的コンパイル, スクリプト, プラグイン, メタプログラミング
画面キャプチャ
■今回作ったソフトの仕様概要
テキストボックスのコードの、最初のpublic classの、String
を引数にとるRun
メソッドを実行するようにした。
■動作#1
ボタンを押すと、Hello, World!!!
とコンソールに出力される。
(Run
メソッドの引数に"World!!!"
を渡している。)
■動作#2
コンパイルエラー情報も出せるようにしている。
4行目の末尾の)
を意図的に消して、public void Run(string s
としてボタンを押すと、
Line:4
Column:29
ErrorNumber:CS1026
ErrorText:) が必要です。
とコンソールに出力される。
ソースコード
usingMicrosoft.CSharp;// C# プラグインの処理に必要usingSystem;usingSystem.CodeDom.Compiler;// C# プラグインの処理に必要usingSystem.IO;usingSystem.Linq;usingSystem.Reflection;usingSystem.Drawing;usingSystem.Windows.Forms;classPluginTest:Form{Buttonbtn;TextBoxtxt;PluginTest(){btn=newButton();btn.Size=newSize(200,30);btn.Text="Compile and Go.";btn.Click+=(s,e)=>{CompileAndGo();};Controls.Add(btn);txt=newTextBox();txt.Multiline=true;txt.ScrollBars=ScrollBars.Both;txt.Top=40;txt.Text=String.Join("\r\n",newstring[]{"using System;","public class TestClass","{"," public void Run(string s)"," {"," Console.Write(\"Hello, \");"," Console.WriteLine(s);"," }","}",""});Controls.Add(txt);Load+=(s,e)=>{MyResize();};Resize+=(s,e)=>{MyResize();};ResizeEnd+=(s,e)=>{MyResize();};}voidMyResize(){inth=ClientSize.Height-txt.Top;if(h<20){h=20;}txt.Size=newSize(ClientSize.Width,h);}voidCompileAndGo(){stringsourceCode=txt.Text;RunCSharp(sourceCode);}staticvoidRunCSharp(stringsourceCode){// C# のコードをアセンブリに変換Assemblyassembly=CompileCSharpCode(sourceCode);if(assembly!=null){// アセンブリに変換されたプラグインを実行RunAssembly(assembly);}}publicstaticAssemblyCompileCSharpCode(stringcsharpSourceCode){using(varcscp=newCSharpCodeProvider()){varparam=newCompilerParameters{GenerateInMemory=true};try{CompilerResultsresult=cscp.CompileAssemblyFromSource(param,csharpSourceCode);if(result.Errors.Count>0){foreach(CompilerErrorcompErrinresult.Errors){//Console.WriteLine("FileName:{0}", compErr.FileName);Console.WriteLine("Line:{0}",compErr.Line);Console.WriteLine("Column:{0}",compErr.Column);Console.WriteLine("ErrorNumber:{0}",compErr.ErrorNumber);Console.WriteLine("ErrorText:{0}",compErr.ErrorText);}returnnull;}returnresult.CompiledAssembly;}catch(Exceptione){Console.WriteLine(e);returnnull;}}}staticvoidRunAssembly(AssemblypluginAssembly){// アセンブリを読み込み、その中から public な最初のクラスを取り出すvarpluginType=pluginAssembly.GetExportedTypes().FirstOrDefault(type=>type.IsClass);if(pluginType!=null){dynamicmyInstance=Activator.CreateInstance(pluginType);Typetype=myInstance.GetType();MethodInfomi=type.GetMethod("Run",newType[]{typeof(String)});if(mi==null){// Stringクラスを引数とするRunメソッドがあるかをチェックMessageBox.Show("Cannot find Run(String) method.");}else{myInstance.Run("World!!!");}}}[STAThread]staticvoidMain(string[]args){Application.Run(newPluginTest());}}