Autor Beitrag
BCT1
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 36



BeitragVerfasst: So 09.01.11 21:26 
Hi, ich will in meinem Projekt einen String als Code laufen lassen und hab da bisher folgendes:
ausblenden 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


Zuletzt bearbeitet von BCT1 am Mo 10.01.11 19:33, insgesamt 1-mal bearbeitet
Yogu
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2598
Erhaltene Danke: 156

Ubuntu 13.04, Win 7
C# (VS 2013)
BeitragVerfasst: So 09.01.11 21:52 
Hallo,

verwendest du die Bibliothek Evaluator? 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 36



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4799
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: 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:
ausblenden C#-Quelltext
1:
options.ReferencedAssemblies.Add("System.Windows.Forms.dll");					
BCT1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 36



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2598
Erhaltene Danke: 156

Ubuntu 13.04, Win 7
C# (VS 2013)
BeitragVerfasst: Mo 10.01.11 19:56 
Hallo,

verwende die Methode GetType von AssemblyResults und GetMethod von TypeResults:

ausblenden C#-Quelltext
1:
results.GetType("Dein Typbezeichner").GetMethod("Dein Methodenbezeichner").Invoke();					

Grüße,
Yogu
BCT1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 36



BeitragVerfasst: Mo 10.01.11 20:18 
Also, ich habs jetzt erstmal so versucht:
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2598
Erhaltene Danke: 156

Ubuntu 13.04, Win 7
C# (VS 2013)
BeitragVerfasst: Mo 10.01.11 20:24 
user profile iconBCT1 hat folgendes geschrieben Zum zitierten Posting springen:
Es kommt wieder der "Eine Ausnahme vom Typ "Evaluator.CompilationException" wurde ausgelöst." Fehler.

Wie lautet denn die Meldung der Exception?
BCT1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 36



BeitragVerfasst: Mo 10.01.11 20:27 
Da steht nur "CompilationExeption was unhandled - Eine Ausnahme vom Typ "Evaluator.CompilationException" wurde ausgelöst."
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4799
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mo 10.01.11 20:30 
Steht evtl. in der Eigenschaft "InnerException" der CompilationException etwas genaueres drin?
BCT1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 36



BeitragVerfasst: Mo 10.01.11 20:31 
Ne, in der InnerExeption steht leider auch nichts
BCT1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 36



BeitragVerfasst: Mi 19.01.11 20:00 
Weiß denn keiner wo der Fehler liegt? Ich hab diese Seite hier gefunden: www.forum-3dcenter.o...ex.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
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4799
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Do 20.01.11 15:05 
Welcher Code (als string) erzeugt denn diesen Fehler (du hast ja vorher geschrieben, daß es mit einfachem Code funktioniert)?
Verwendest du denn die ausgeschriebenen Namespaces? Bei der Evaluation-Library scheint es z.Z. ja nicht möglich zu sein "using"-Blöcke hinzuzufügen (s.a. www.codeproject.com/...on-namespaces.aspx).
BCT1 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 36



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4799
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Do 20.01.11 19:24 
Hi,

wie schon geschrieben, mußt du den gesamten Namensbereich angeben, d.h. probiere mal
ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 36



BeitragVerfasst: Do 20.01.11 19:49 
Hi, ich hab das jetzt so eingegeben:
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4799
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 36



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4799
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Do 20.01.11 22:18 
Du brauchst doch nur die beiden Methoden in dein Programm kopieren und dann folgendermaßen aufrufen:
ausblenden C#-Quelltext
1:
2:
3:
Assembly assembly = CreateAssembly(sSourceCode);

CallEntry(assembly, "Execute"); // oder wie auch immer deine Methode heißt


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:
ausblenden 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()
    {
      // Hier kommt jetzt der vom Anwender eingegebene Code hin:
      MessageBox.Show("Test");
      // Ende (Gelände)
    }
  }
}

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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 561
Erhaltene Danke: 137



BeitragVerfasst: Do 20.01.11 22:52 
Hi, habe auch mal versucht, nach Vorlage von dotnet-snippets.de/d...laufzeit-SID986.aspx was zu coden. Und das sieht so aus:
ausblenden 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:
ausblenden 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