LoginSignup
11

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-11-20

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

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

画面キャプチャ

image.png

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

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

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


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

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

ソースコード


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());
    }
}

参考サイト

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11