C#,JScript, VisualBasic 을 스크립트처럼 사용하기




설명


어떤 프로그램을 만들다 보면, 간혹 사용자로부터 소스코드를 입력받아 실행시켜야 하는 경우가 있습니다. 물론 이런 기능을 이용할 경우가 많치는 않지만요. :) 과연 가능할까요? 답부터 말씀 드리자면 가능합니다.




CodeDomProvider


아시다시피, .NET 은 인터프리터 언어가 아니라, 컴파일 언어입니다. 엄격히 말해, 스크립트로 실행되는게 아니라는 것이지요. 그럼 어떻게 하느냐? CodeDomProvider 라는 녀석을 이용해서, Runtime 시점에 코드를 빌드하고 실행하는 것입니다. 마치 스크립트로 동작하는 것 처럼 말이죠.


CodeComProvider 는 대표적으로 C#, JScript, VisualBasic 모두 지원합니다. 타 언어도 되는걸로 알고 있습니다만, 찾아보시기 바랍니다. :)


코드를 보여드리기 전에, 간략히 설명 드리자면...

1. 먼저 코드를 파일이나 string 형태로 받아와서 빌드를 합니다.

2. Assembly 를 얻습니다.

3. EnryPoint 를 통해서 실행하거나, Type 을 통해서 Instance 를 생성 후, 메소드를 호출합니다.


이런 구조입니다. 외부에서 인지하기에는 마치 스크립트로 동작하는 것 처럼 느낄 수 있습니다. 물론 빌드 속도가 엄청 느리지만 않다면 말이죠 :) 코드를 보시면 쉽게 이해 하실 수 있을 것 입니다.




    [Sample Code]


public static void Main(string[] argv)
{
    string[] code = new string[]
    {
        "using System;" +
        "public class CSharScript {" +
        "public static int Main() { return 10; }" +
        "public static string StaticMethod() { return \"StaticMethod\"; }" +
        "public string MemberMethod(string input) { return input; }" +
        "}"
    };

    ExecuteScript(code);
}

static void ExecuteScript(string[] sources) {
    // create provider (사용 후, Dispose 해주어야 합니다.)
    using (CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp")) {
        /* 이외에도 각 언어별 provider 가 존재합니다.
            * CodeDomProvider provider = CodeDomProvider.CreateProvider("VisualBasic");
            * CodeDomProvider provider = CodeDomProvider.CreateProvider("JScript");
            * 
            * 
            * 필요에 따라, 아래처럼 직접 CSharpCodeprovider 를 생성해도 됩니다.
            * CSharpCodeProvider provider = new CSharpCodeProvider();
            * VBCodeProvider provider = new VBCodeProvider();
            * JScriptCodeProvider provider = new JScriptCodeProvider();
            * 
            * 하지만 전자쪽 방법을 추천드립니다. 문자열로 생성시 별도로 Assembly 를 추가할 필요도 없고,
            * using 을 할 필요도 없는 반면, 후자 방식은 일일이 전부 추가해 주어야 합니다. :)
        */

        CompilerParameters options = new CompilerParameters() {
            //CompilerOptions = "/optimize",
            GenerateExecutable = true, // 실행 가능하도록 만듦. 즉 Main 메소드가 있는 exe 파일 같이 만들거냐는 것입니다.
            // false 인 경우, DLL 처럼 메소드 집합체(?)가 만들어 집니다.
            GenerateInMemory = false,
            MainClass = "CSharScript2",
            IncludeDebugInformation = false,
            TreatWarningsAsErrors = true
        };

        // Compile
        CompilerResults results = provider.CompileAssemblyFromSource(options, sources);


        if (results.Errors.Count > 0) {
            string output = "Compile Error(s): ";
            foreach (CompilerError error in results.Errors) {
                output += string.Format("{0}{1}", Environment.NewLine, error.ErrorText);
            }
            throw new ArgumentException("Compile Error", output);
        }


        // GenerateExecutable 이 true 인 경우에만 EntryPoint 로 접근 가능합니다.
        MethodInfo entryPoint = results.CompiledAssembly.EntryPoint;
        Type mainClass = entryPoint.DeclaringType;
        object result = entryPoint.Invoke(null, null);
        Console.WriteLine("EntryPoint Result: " + result);



        // GenerateExecutable 이 false 인 경우에도 가능합니다.
        Type CSharpScriptClass = results.CompiledAssembly.GetType("CSharScript");
        var instance = Activator.CreateInstance(CSharpScriptClass);
        result = CSharpScriptClass.GetMethod("MemberMethod").Invoke(instance, new object[] { "Hello, World" });
        Console.WriteLine("MemberMethod " + result);

        // JScript 는 아래처럼 JScriptEvaluate 라는 static method 를 통해서 바로 실행할 수도 있습니다.
        result = Microsoft.JScript.Eval.JScriptEvaluate(sources[0], Microsoft.JScript.Vsa.VsaEngine.CreateEngine());
    }
}







'Microsoft > C#' 카테고리의 다른 글

Implicit Operator  (0) 2014.11.17
BlockingQueue  (0) 2014.05.19
#warning, #error, #line 지시자  (0) 2014.05.08
인증서(certification) 만들기  (1) 2014.04.09
윈도우즈 방화벽 규칙 추가/설정/삭제하기  (3) 2014.03.27