Entwickler-Ecke
C# - Die Sprache - CreateVirtualMethod mit Namespace
BCT1 - So 09.01.11 21:26
Titel: CreateVirtualMethod mit Namespace
Hi, ich will in meinem Projekt einen String als Code laufen lassen und hab da bisher folgendes:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25:
| class Code { public static bool Prüfen(string code) { MethodResults stringChanger = null;
StringBuilder source = new StringBuilder(); source.Append("public bool Prüfe()"); source.Append(Environment.NewLine); source.Append("{"); source.Append(Environment.NewLine); source.Append(code); source.Append(Environment.NewLine); source.Append("}");
stringChanger = Eval.CreateVirtualMethod( new CSharpCodeProvider().CreateCompiler(), source.ToString(), "Prüfe", new CSharpLanguage(), false);
return (bool)stringChanger.Invoke(); } } |
Mit dem Parameter code kommt der String den ich ausgeführt haben will z.B. einfach nur "return true;". Das funktioniert auch gut, nur wenn ich in diesen String Eigenschaften aus öffendlichen Klassen reinpacke, geht das nicht. ZB wenn ich schreibe "MessageBox.Show("hallo");" gibt der mir diesen Fehler: "Eine Ausnahme vom Typ "Evaluator.CompilationException" wurde ausgelöst." Ich suche jetzt einen Weg da Namespaces mit reinzubringen, damit ich auf System.Windows...MessageBox.Show zugreifen kann.
Hoffe Ihr könnt mir helfen
Yogu - So 09.01.11 21:52
Hallo,
verwendest du die Bibliothek
Evaluator [
http://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=3445]? Oder eine geänderte Version davon? Du rufst in deinem Quelltext
Eval.CreateVirtualAssembly auf, speicherst den Rückgabewert aber in einer Variable vom Typ
MethodResults, das von
Eval.CreateVirtualMethod verwendet wird.
Du könntest entweder versuchen,
using-Direktiven vor die Methode zu stellen (ggf. musst du statt einer Methode einen Typ oder eine Assembly erstellen), oder du greifst einfach direkt über z.B.
System.Windows.Forms.MessageBox auf die Klassen zu.
Aber brauchst du wirklich Evaluation? Das wird eigentlich kaum, verwendet, höchstens wenn der Benutzer selbst C#-Code eingeben soll, aber keine Plugins schreiben will. Wo werden denn die Quelltexte für den Evaluator erstellt?
BCT1 - So 09.01.11 22:45
Die Texte werden vom Benutzer selbst eingegeben und ja ich benutze die Evaluator Bibliothek. Das mit dem direkten aufruf über System.Windows.Forms.MessageBox geht irgendwie nicht.
Was wenn ich einen Typ oder Assembly erstelle, wie müsste das dann mit den Parametern in Eval.CreateVirtualAssembly() bzw Eval.CreateVirtualType() aussehen? Ich komm damit irgendwie nicht klar.
Th69 - Mo 10.01.11 12:35
Du scheinst irgendwie andere Parameter bei CreateVirtualAssembly zu benutzen als im Artikel beschrieben:
Zitat: |
public static AssemblyResults CreateVirtualAssembly(ICodeCompiler compiler,
string assemblySource,
bool debug,
Language language,
params string[] references);
|
???
Der letzte Parameter "string[] references" sollte eigentlich eine Liste der von dir referenzierten Assemblies sein, d.h. z.B. "System.Windows.Forms.dll".
Alternativ kann man auch über die Compiler-Optionen (bei den anderen Methoden mit dem Parameter 'CompilerParameters options') diese Liste füllen:
C#-Quelltext
1:
| options.ReferencedAssemblies.Add("System.Windows.Forms.dll"); |
BCT1 - Mo 10.01.11 19:43
Stimmt, es sollte eigendlich heißen CreateVirtialMethod. Habs oben mal geändert. Und meine frage ist jetzt da Eval.CreateVirtualAssembly einen Rückgabewert von AssemblyResults hat wie ich wieder dieses schöne MethodResults result.Invoke() hinkriege. AssemblyResults hat diese Methode ja nicht.
Yogu - Mo 10.01.11 19:56
Hallo,
verwende die Methode
GetType von
AssemblyResults und
GetMethod von
TypeResults:
C#-Quelltext
1:
| results.GetType("Dein Typbezeichner").GetMethod("Dein Methodenbezeichner").Invoke(); |
Grüße,
Yogu
BCT1 - Mo 10.01.11 20:18
Also, ich habs jetzt erstmal so versucht:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| StringBuilder source = new StringBuilder(); source.Append("public void Execute()"); source.Append(Environment.NewLine); source.Append("{"); source.Append(Environment.NewLine); source.Append(code); source.Append(Environment.NewLine); source.Append("}");
System.CodeDom.Compiler.CompilerParameters options = new System.CodeDom.Compiler.CompilerParameters(); options.ReferencedAssemblies.Add("System.Windows.Forms.dll");
MethodResults stringChanger = Eval.CreateMethod(new CSharpCodeProvider().CreateCompiler(), source.ToString(), "Execute", options, new CSharpLanguage()); stringCharger.Invoke(); |
Der code den ich oben reingebe hat keine Fehler aber trotzdem kann er das nicht Ausführen. Es kommt wieder der "Eine Ausnahme vom Typ "Evaluator.CompilationException" wurde ausgelöst." Fehler.
Yogu - Mo 10.01.11 20:24
BCT1 hat folgendes geschrieben : |
Es kommt wieder der "Eine Ausnahme vom Typ "Evaluator.CompilationException" wurde ausgelöst." Fehler. |
Wie lautet denn die Meldung der Exception?
BCT1 - Mo 10.01.11 20:27
Da steht nur "CompilationExeption was unhandled - Eine Ausnahme vom Typ "Evaluator.CompilationException" wurde ausgelöst."
Th69 - Mo 10.01.11 20:30
Steht evtl. in der Eigenschaft "InnerException" der CompilationException etwas genaueres drin?
BCT1 - Mo 10.01.11 20:31
Ne, in der InnerExeption steht leider auch nichts
BCT1 - Mi 19.01.11 20:00
Weiß denn keiner wo der Fehler liegt? Ich hab diese Seite hier gefunden:
http://www.forum-3dcenter.org/vbulletin/archive/index.php/t-398517.html nur leider weiß ich auch nicht wie ich das benutzen soll, da das ja schon fortgeschrittene Programmierung ist. Vielleicht kennt ja jemand auch eine andere Möglichkeit wie man das sonst noch machen könnte?
Ich hoffe ihr könnt mir helfen, denn ich brauch das dringend
BCT1 - Do 20.01.11 18:28
Ja, wenn ich bei meinem code oben return true; reinschreibe geht er ja in die Methode "Prüfe()" die einen bool-Wert wiedergibt. Da ich ja nur return true geschrieben hab gibt er halt true zurück. Dieser code kann auch noch komplizierter sein nur ohne Parameter von außen lässt sich da ja nicht viel machen. Nur wenn ich jetzt auf namespaces zugreifen will oder interne Variablen verwenden will geht das nicht. Ich bräuchte also eine Möglichkeit um das namespace da mit einzubinden und somit auf Dinge wie interne Variablen(um diese zu vergleichen) zugreifen zu können oder halt MessageBoxen aufrufen zu lassen(zb um zu prüfen ob der Code bis zu bestimmten Stellen einwandfrei läuft). Mein Problem ist einfach, dass ich using-Direktiven nicht einfach vor source.Append("public bool Prüfe()"); schreiben kann weil er mir dann einen Fehler bei der Eval.CreateVirtualMethod() -Methode gibt und die Eval.CreateVirtualAssembly() versuch ich nach bestem Gewissen(und Wissen) zusammenzuflicken aber ich hab echt keinen Plan wie da mein stringBuilder (aus dem Oberen Codeausschnitt) aussehen sollte.
Th69 - Do 20.01.11 19:24
Hi,
wie schon geschrieben, mußt du den gesamten Namensbereich angeben, d.h. probiere mal
C#-Quelltext
1:
| System.Windows.Forms.MessageBox.Show("Hallo"); |
Um auch auf Variablen deines eigenen Programms zuzugreifen, mußt du die EXE selber als Referenz hinzufügen und dann auch wieder den kompletten Namensbereich angeben.
Besser wäre aber, du übergibt die Daten als Parameter an deine Prüfe()-Methode (P.S. Umlaute würde ich nicht als Namensbezeichner verwenden).
BCT1 - Do 20.01.11 19:49
Hi, ich hab das jetzt so eingegeben:
C#-Quelltext
1:
| bool variable = Code.Prüfen("System.Windows.Forms.MessageBox.Show(\"Hallo\"); return true;"); |
aber er gibt mir wieder einen Fehler wo auch wieder nichts in der InnerExeption drinsteht.
Was das angeben der Exe als Referenz angeht, wie würde man das denn machen?
Th69 - Do 20.01.11 21:49
Sorry, dann würde ich mal versuchen, auf die Evaluator-Klasse zu verzichten und selber den CodeDOM-Compiler zu benutzen (wie in dem von dir geposteten Link mit den Methoden CreateAssembly und CallEntry).
BCT1 - Do 20.01.11 22:02
Ja, nur das Problem is, dass ich mich mit sowas fortgeschrittenem nicht auskenne. Und da es nich so viele mit diesen Problemen gibt, kann man da auch net so viel drüber finden. Ich fänds echt cool wenn einer von euch mir das mal erklären könnte/bzw code schreiben könnte. Das wäre echt nett
Th69 - Do 20.01.11 22:18
Du brauchst doch nur die beiden Methoden in dein Programm kopieren und dann folgendermaßen aufrufen:
C#-Quelltext
1: 2: 3:
| Assembly assembly = CreateAssembly(sSourceCode);
CallEntry(assembly, "Execute"); |
Und als Parameter 'sSourceCode' übergibt du dann den mit deinem StringBuilder (aus deinem Anfangsbeitrag) erzeugten Code (Hinzufügen
mußt du jedoch noch einen Namespace sowie eine Klasse!!!).
Und hierbei kannst du dann auch noch "using"-Blöcke verwenden, d.h. dein übergebender Code sieht dann in etwa so aus:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| using System; using System.Windows.Forms;
namespace TestApp { class Test { static void Execute() { MessageBox.Show("Test"); } } } |
Du mußt also die "using"-Blöcke (wie im normalen C#-Code auch), an den Anfang deines Codes stellen (d.h. evtl. vom Anwender dies noch gesondert eingeben lassen).
So ich hoffe, dies war jetzt ausführlich genug erklärt? -)
Trashkid2000 - Do 20.01.11 22:52
Hi, habe auch mal versucht, nach Vorlage von
http://dotnet-snippets.de/dns/kompilierung-zur-laufzeit-SID986.aspx was zu coden. Und das sieht so aus:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:
| public static Assembly Prüfen(string userInput) { CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); CompilerParameters cp = new CompilerParameters(); cp.ReferencedAssemblies.Add("System.dll"); cp.ReferencedAssemblies.Add("System.Windows.Forms.dll"); cp.CompilerOptions = "/t:library"; cp.GenerateInMemory = true; StringBuilder sb = new StringBuilder(); sb.AppendLine(@"using System; using System.Windows.Forms; namespace myNamespace { public class Test { public bool Prüfe(string input) {"); sb.AppendLine( userInput); sb.AppendLine(@" } } }"); CompilerResults cr = provider.CompileAssemblyFromSource(cp, sb.ToString()); if (cr.Errors.Count > 0) { MessageBox.Show(cr.Errors[0].ErrorText); return null; } return cr.CompiledAssembly; } |
Die Einrückung ist so gemacht, damit man besser die Struktur der entstandenen Assembly sieht.
Und die Benutzung der Prozedur sieht dann wie folgt aus:
C#-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| string userInput = "MessageBox.Show(\"Bla\");return true;"; Assembly assembly = Prüfen(userInput); if (assembly != null) { object dynamicAssembly = assembly.CreateInstance("myNamespace.Test"); Type type = dynamicAssembly.GetType(); MethodInfo mi = type.GetMethod("Prüfe"); object result = mi.Invoke(type, new object[] { userInput }); MessageBox.Show(result); } |
LG, Marko
BCT1 - Fr 21.01.11 20:11
Ah, es funktioniert endlich! Einen herzlichen dank an euch alle. Alleine hätte ich das nie geschafft
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!