Manchmal kann es notwendig sein, dass man einen bestimmten Code zur explizit zur Laufzeit kompiliert. Man könnte sich daraus zum Beispiel eine kleine Shell bauen. Wie ihr das bewerkstelligen könnt, zeige ich euch heute.
Ihr braucht dafür einen MemoryStream, einer StreamWriter und den Namensraum System.Reflection, sowie System.CodeDom.Compiler.
Das ganze funktioniert folgendermaßen: Ihr erzeugt einen Quellcode der durch die gleich gezeigt Methode im Speicher kompiliert und ausgeführt wird. Um euch die Arbeit zu erleichtern, könnt ihr schon vornherin ein paar Definitionen vornehmen, wie z.b. die zu verwendenden Namensräume, den Funktionsrumpf etc.. In meinem Snippet habe ich ein WindowsForms-Projekt erstellt, daher benutze ich bewusst die MessageBox.
/// <summary>
/// Führt die Compilierung mit dem angegebenen Code aus
/// </summary>
private void Compile()
{
String InputCode = String.Empty;
//Unser TestCode, in dem Wir ein MessageBox aufrufen
InputCode = "MessageBox.Show((1 + 2 + 3).ToString());";
System.Reflection.Assembly Assembly = CompileCode(InputCode);
//Compilefehler abfangen
if (Assembly == null) return;
object Temp = Assembly.CreateInstance("RunTimeCompiler.Test");
//Fehler bei Instanzenerzeugung
if (Temp == null) return;
Type RefType = Temp.GetType();
//Aufzurufende Methode auswählen, in unserem Fall heißt die Funktion Ergebnis
System.Reflection.MethodInfo MethodInfo = RefType.GetMethod("Ergebnis");
//Methode aufrufen, in unserem Fall haben wir in der Funktion Ergebnis keine Parameter. Andernfalls müssten diese als Object-Array angegeben Werden
MethodInfo.Invoke(Temp, new object[] { });
}
/// <summary>
///
/// </summary>
/// <param name="InputCode"></param>
/// <returns></returns>
public static System.Reflection.Assembly CompileCode(string InputCode)
{
System.CodeDom.Compiler.CodeDomProvider CodeDomProvider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp");
//Parameter für die Compilierung, wie die einzubindenen Bibliotheken usw.
System.CodeDom.Compiler.CompilerParameters CompilerParameters = new System.CodeDom.Compiler.CompilerParameters();
CompilerParameters.ReferencedAssemblies.Add("System.dll");
CompilerParameters.ReferencedAssemblies.Add("System.Windows.Forms.dll");
CompilerParameters.CompilerOptions = "/t:library";
CompilerParameters.GenerateInMemory = true;
//Über den StringBuilder wird der Code zusammengesetzt
StringBuilder Temp = new StringBuilder();
Temp.AppendLine(@"using System;");
Temp.AppendLine(@"using System.Windows.Forms;");
Temp.AppendLine(@"namespace RunTimeCompiler{");
Temp.AppendLine(@"public class Test{");
Temp.AppendLine(@"public void Ergebnis(){");
Temp.AppendLine(InputCode);
Temp.AppendLine(@"}}}");
//Compilieren
System.CodeDom.Compiler.CompilerResults CompilerResults = CodeDomProvider.CompileAssemblyFromSource(CompilerParameters, Temp.ToString());
//Auf CompilerFehler prüfen
if (CompilerResults.Errors.Count > 0)
{
MessageBox.Show(CompilerResults.Errors[0].ErrorText, "Fehler bei Laufzeitkompilierung", MessageBoxButtons.OK, MessageBoxIcon.Error);
return null;
}
//Rückgabe der compilierten Assembly
return CompilerResults.CompiledAssembly;
}
2 Kommentare zum Snippet