C#で作ったプログラムから、C#のソースコードをコンパイルして実行する

C#で作ったexe上で、C#のソースコードをコンパイル・実行できる。ライブラリ不要！
※何でもできるので、わりと危険。

きーわーど：　動的コンパイル, スクリプト, プラグイン, メタプログラミング

# 画面キャプチャ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438183/b420eb37-bfa7-7246-82b9-7ad4b6f6ab0a.png)

■今回作ったソフトの仕様概要
テキストボックスのコードの、最初のpublic classの、`String`を引数にとる`Run`メソッドを実行するようにした。

■動作#1
ボタンを押すと、`Hello, World!!!`とコンソールに出力される。
（`Run`メソッドの引数に`"World!!!"`を渡している。）

■動作#2
コンパイルエラー情報も出せるようにしている。
4行目の末尾の`)`を意図的に消して、`public void Run(string s`としてボタンを押すと、

```

Line:4
Column:29
ErrorNumber:CS1026
ErrorText:) が必要です。
```

とコンソールに出力される。

# ソースコード

```csharp

using Microsoft.CSharp; // C# プラグインの処理に必要
using System;
using System.CodeDom.Compiler; // C# プラグインの処理に必要
using System.IO;
using System.Linq;
using System.Reflection;
using System.Drawing;
using System.Windows.Forms;

class PluginTest : Form
{
    Button btn;
    TextBox txt;

    PluginTest()
    {
        btn = new Button();
        btn.Size = new Size(200, 30);
        btn.Text = "Compile and Go.";
        btn.Click += (s,e)=>{CompileAndGo();};
        Controls.Add(btn);

        txt = new TextBox();
        txt.Multiline = true;
        txt.ScrollBars = ScrollBars.Both;
        txt.Top = 40;
        txt.Text = String.Join("\r\n", new string[]{
                    "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();};
    }

    void MyResize()
    {
        int h = ClientSize.Height - txt.Top;
        if(h<20){h=20;}
        txt.Size = new Size(ClientSize.Width, h);
    }
 
    void CompileAndGo()
    {
        string sourceCode = txt.Text;
        RunCSharp(sourceCode);
    }

    static void RunCSharp(string sourceCode)
    {
        // C# のコードをアセンブリに変換
        Assembly assembly = CompileCSharpCode(sourceCode);
        if (assembly != null) {
            // アセンブリに変換されたプラグインを実行
            RunAssembly(assembly);
        }
    }
 
    public static Assembly CompileCSharpCode(string csharpSourceCode)
    {
        using (var cscp = new CSharpCodeProvider()) {
            var param = new CompilerParameters { GenerateInMemory = true };
            try {
                CompilerResults result = cscp.CompileAssemblyFromSource(param, csharpSourceCode);
                if ( result.Errors.Count > 0 ) {
                    foreach (CompilerError compErr in result.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);
                    }
                    return null;
                }
                return result.CompiledAssembly;
            }
            catch(Exception e) {
                Console.WriteLine(e);
                return null;
            }
        }
    }
 
    static void RunAssembly(Assembly pluginAssembly)
    {
        // アセンブリを読み込み、その中から public な最初のクラスを取り出す
        var pluginType = pluginAssembly.GetExportedTypes().FirstOrDefault(type => type.IsClass);
        if (pluginType != null) {
            dynamic myInstance = Activator.CreateInstance(pluginType);

            Type type = myInstance.GetType();
            MethodInfo mi = type.GetMethod("Run", new Type[]{typeof(String)} );
            if (mi == null) { // Stringクラスを引数とするRunメソッドがあるかをチェック
                MessageBox.Show("Cannot find Run(String) method.");
            }
            else {
                myInstance.Run("World!!!");
            }
        }
    }

    [STAThread]
    static void Main(string[] args)
    {
        Application.Run(new PluginTest());
    }
}

```


# 参考サイト

- http://blog.shos.info/archives/2012/12/csharp_dynamic4.html
- https://dobon.net/vb/dotnet/programing/incodecompiler.html