Entwickler-Ecke

Basistechnologien - C#-Code aus externer Textdatei ausführen


KanneM - Mo 19.03.12 12:07
Titel: C#-Code aus externer Textdatei ausführen
Liebe Community,

Ich suche schon lange nach einer (hoffentlich existierenden) Lösung für mein Problem, habe aber nie etwas passendes gefunden:

Ich möchte gerne, dass ein Programm einen C#-Code aus z.B. einer Textdatei einliest und anschließend ausführt (Annahme: Code ist fehlerfrei). Also praktisch ein extrerner Code den man in ein Hauptprogramm integriert, jedoch dann variabel... Existiert so eine Möglichkeit überhaupt oder ist das unlösbar? O.o

Mit freundlichen Grüßen,
Kanne


daeve - Mo 19.03.12 12:11

Für was brauchst du den so was ?
gewisse Teile des Codes die anpassbar sein müssen, kann man über ein Konfigurationsfile erledigen...


Ralf Jansen - Mo 19.03.12 12:21

Einfach den Code durch den Compiler jagen(siehe z.b. hier [http://support.microsoft.com/kb/304655/de])? Der kompilierte Code sollte dann irgendeiner Definition folgen damit du weißt wie das Kompilat dann auszuführen ist. Also zum Beispiel könnten die Klassen in diesem Code eine bestimmtes Interface implementieren nach dem du im Kompilat suchen kannst um dann eine bestimmte Methode an diesem Interface aufzurufen um in auszuführen.


Th69 - Mo 19.03.12 13:37

Hallo KanneM,

die von Ralf genannte Möglichkeit nennt sich CodeDOM. Unter Stringwerte in ausführbaren Code umwandeln [http://www.c-sharp-forum.de/viewtopic.php?p=654229#654229] (sowie Programm, um Programme zu erstellen [http://www.c-sharp-forum.de/viewtopic.php?p=640316#640316]) habe ich noch weitere Artikel dazu aufgelistet.


KanneM - Mo 19.03.12 15:47

user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Hallo KanneM,

die von Ralf genannte Möglichkeit nennt sich CodeDOM. Unter Stringwerte in ausführbaren Code umwandeln [http://www.c-sharp-forum.de/viewtopic.php?p=654229#654229] (sowie Programm, um Programme zu erstellen [http://www.c-sharp-forum.de/viewtopic.php?p=640316#640316]) habe ich noch weitere Artikel dazu aufgelistet.


Habs mal ausprobiert, klappt wunderbar =)

Nur eins noch: Das direkte einbinden in die aktuelle Anwendung geht nicht, oder? Also quasi: "FuehreAus(string CodeString)"
Ich meine klar, "http://blogs.msdn.com/b/thottams/archive/2006/08/16/701872.aspx" und dann die .exe ausführen lassen geht, aber es wäre doch schöner wenn die Anwendung direkt den String-Code ausführen würde.

MfG,
Kanne


Ralf Jansen - Mo 19.03.12 16:02

Zitat:
aber es wäre doch schöner wenn die Anwendung direkt den String-Code ausführen würde.


Bei diesem Code aus deinem Link sollte es doch eigentlich bei dir klingeln was zu ändern wäre um deinem Wunsch nahe zu kommen.


C#-Quelltext
1:
2:
3:
4:
5:
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = true;
cp.OutputAssembly = "Result.exe";
cp.GenerateInMemory = false;
CompilerResults cr = provider.CompileAssemblyFromSource(cp, sourcecode);


KanneM - Mo 19.03.12 16:06

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Zitat:
aber es wäre doch schöner wenn die Anwendung direkt den String-Code ausführen würde.


Bei diesem Code aus deinem Link sollte es doch eigentlich bei dir klingeln was zu ändern wäre um deinem Wunsch nahe zu kommen.


C#-Quelltext
1:
2:
3:
4:
5:
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = true;
cp.OutputAssembly = "Result.exe";
cp.GenerateInMemory = false;
CompilerResults cr = provider.CompileAssemblyFromSource(cp, sourcecode);


Ich denke schon... Bin mir aber nicht sicher, in unseren Vorlesungen sind wir noch nicht so tief eingetaucht.


C#-Quelltext
1:
2:
3:
4:
5:
CompilerParameters cp = new CompilerParameters();
cp.GenerateExecutable = false;
//cp.OutputAssembly = "Result.exe";
cp.GenerateInMemory = true;
CompilerResults cr = provider.CompileAssemblyFromSource(cp, sourcecode);


Würde das dann so funktionieren?

LG,
Kanne


Ralf Jansen - Mo 19.03.12 16:18

Theoretisch. In den CompilerResults solltest du jetzt deine Assembly finden.


KanneM - Mo 19.03.12 17:47

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Theoretisch. In den CompilerResults solltest du jetzt deine Assembly finden.

D.h. ich könnte es jetzt wie aufrufen? Sorry, aber ich bin nochnet so fit in dem Gebiet, arbeite weng vor...


Th69 - Mo 19.03.12 20:42

Nur ein paar Stichworte, da ich jetzt nicht viel Zeit habe:

C#-Quelltext
1:
2:
3:
4:
5:
CompilerResults.CompiledAssembly
CreateInstance
GetMethod
Invoke
...


Als Beispiel auf die Schnelle habe ich http://stackoverflow.com/questions/5997995/in-net-4-0-how-do-i-sandbox-an-in-memory-assembly-and-execute-a-method gefunden

Viel Erfolg noch dabei


KanneM - Di 20.03.12 13:12

Also ich hab mal weng was durchprobiert, letztenendes endet es immer in einem Fehler... Glaube auch nicht dass ich des richtig angesetzt habe...
Könntest du mir vllt zeigen wie der Code aussehen würde, dann könnte ich mein Projekt bis nächste Woche noch fertig stellen =/

LG,
Kanne


Th69 - Di 20.03.12 14:32

Hallo KanneM,

machen wir es besser umgekehrt. Du präsentierst deinen bisherigen (kompilierfähigen) Code und dann schauen wir zusammen darauf und geben dir Tipps.


KanneM - Di 20.03.12 16:22

Wiegesagt, die Änderung die ich oben gepostet habe ist das was funktioniert, das dann aber zum laufen bringen ist die Schwierigkeit. Ich hab mit Activator.Create... und ... .CreateInstance rumprobiert, ein Assemblyverweis brachte auch nicht das gewünschte Resultat.

Wird wohl noch zu hcoh für mich sein, aber ich will das jetzt wenigstens fertig bekommen =D

-------------------------------------------------------------------------------------------------

EDIT: Hab jetzt nochmal was anderes gefunden:
http://msdn.microsoft.com/de-de/library/ms173139.aspx
Hierbei wird wohl aber eine exe Datei geladen, also genau das was ich eigentlich nicht will.
Auch hier
http://stackoverflow.com/questions/5997995/in-net-4-0-how-do-i-sandbox-an-in-memory-assembly-and-execute-a-method
habe ich einiges ausprobiert, aber wie soll ich einen Pfad vom Assembly angeben, wenn doch nur der Code kompiliert wird?

LG


Th69 - Mi 21.03.12 18:17

Hallo KanneM,

was genau willst du denn ausführen? Eine einzelne statische Methode oder aber eine Klasse instantiieren und dann eine Methode davon aufrufen?

Im 2.Link (d.h. den ich auch gepostet habe) sind die entscheidenden Zeilen

C#-Quelltext
1:
2:
3:
var instanceOfSomeClass = compilerResults.CompiledAssembly.CreateInstance(className);

instanceOfSomeClass.GetType().GetMethod("DoSomething").Invoke(instanceOfSomeClass, null);

Wobei className dann der Klassenname ist und "DoSomething" die aufzurufende Methode darstellt (beide vom Typ string).
Für eine statische Methode kannst du den ersten Parameter bei Invoke dann auf null setzen.

Wenn das auch nicht hilft, dann poste doch mal, was du genau kompilieren willst (dies muß natürlich eine komplette Klasse inkl. Referenzen sein).


KanneM - Mi 21.03.12 19:16

Ich bekomme jetzt noch einen Fehler in der zweiten Zeile von deinem Post:

Zitat:
System.NullReferenceException wurde nicht behandelt.
Message=Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.
Source=Test
StackTrace:
bei CodeDomSample.CSharpCodeExample() in C:\Users\XXX\Documents\visual studio 2010\Projects\TestConsole\Test\Program.cs:Zeile 194.
bei CodeDomSample.Main() in C:\Users\XXX\Documents\visual studio 2010\Projects\TestConsole\Test\Program.cs:Zeile 206.
bei System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
bei System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
bei Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
bei System.Threading.ThreadHelper.ThreadStart_Context(Object state)
bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
bei System.Threading.ThreadHelper.ThreadStart()
InnerException:

oder
Zitat:
Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt.


Der 'externe' Code sieht so aus:

C#-Quelltext
1:
String sourcecode = "\nusing System;\npublic class Sample \n{\n    static void Main()\n    {\n        Console.WriteLine(\"This is a test\");\n    }\n}"                    

Und die verarbeitende Methode: (genau wie hier: http://blogs.msdn.com/b/thottams/archive/2006/08/16/701872.aspx)

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:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
public void CSharpCodeExample()
    {

        String sourcecode = "\nusing System;\npublic class Sample \n{\n    static void Main()\n    {\n        Console.WriteLine(\"This is a test\");\n    }\n}";
        //String sourcecode = "using System;public class Sample{static void Main(){Console.WriteLine(\"This is a test 2\");Console.ReadLine();}}";
    

        CSharpCodeProvider provider = new CSharpCodeProvider();

        CompilerParameters cp = new CompilerParameters();

        cp.GenerateExecutable = false;

        //cp.OutputAssembly = "Result.exe";

        cp.GenerateInMemory = true;

        CompilerResults cr = provider.CompileAssemblyFromSource(cp, sourcecode);

        if (cr.Errors.Count > 0)
        {

            Console.WriteLine("Errors building {0} into {1}", sourcecode, cr.PathToAssembly);

            foreach (CompilerError ce in cr.Errors)
            {

                Console.WriteLine("  {0}", ce.ToString());

                Console.WriteLine(); 

            }

        }

        else
        {

            Console.WriteLine("Source \n \n {0} \n \n \n built into {1} successfully.", sourcecode, cr.PathToAssembly);

            var instanceOfSomeClass = cr.CompiledAssembly.CreateInstance("Sample");
            instanceOfSomeClass.GetType().GetMethod("Main").Invoke(instanceOfSomeClass, null);
        }

        return;

    }


PS: Habe bei Invoke den Parameter wie du gesagt hast auch auf null gesetzt, ging trotzdem nicht =(

Verstehe ich eigentlich nicht ganz warum das jetzt nicht geht :x

LG,
Kanne


Ralf Jansen - Mi 21.03.12 19:32

Deine Methode in der zu kompilierenden Klasse ist statisch (Nebenbei die solltest du auch nicht Main nennen. So sollte nur die Start Methode eines Executables heißen aber nicht irgendeine Methode die man aufrufen kann) Dein Code könnte bereits funktionieren wenn du mal das static weg nimmst. Ansonsten die Methode einfach nicht über eine Instanz aufrufen sondern direkt über die Klasse z.B. in etwa so


C#-Quelltext
1:
cr.CompiledAssembly.GetType("Sample").GetMethod("DoSomething", BindingFlags.Public | BindingFlags.Static).Invoke(nullnull);                    


Th69 - Mi 21.03.12 19:35

Hallo,

am besten, du teilst den Code in mehrere Zeilen auf.

Ich nehme an, daß er bei GetMethod("Main"null zurückgibt, da deine Methode nicht public ist.

Also besser darauf abtesten:

C#-Quelltext
1:
2:
3:
MethodInfo methodInfo = instanceOfSomeClass.GetType().GetMethod("Main");
if (methodInfo != null)
  methodInfo.Invoke(instanceOfSomeClass, null);

Ansonsten kannst du bei GetMethod auch noch BindingFlags übergeben:

C#-Quelltext
1:
2:
BindingFlags flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
MethodInfo methodInfo = instanceOfSomeClass.GetType().GetMethod("Main", BindingFlags);

Am besten du schaust dafür in der MSDN nach...


KanneM - Mi 21.03.12 19:58

Ich habe jetzt mal die Version von Ralf Jansen ausprobiert, funktioniert super! =D
Schau mir jetzt auchmal die anderen Lösungen an und experimentier nochweng mit dem Code damit ich das dann auch komplett alles versteh...

Danke an euch und gerade dich Th69 für die Geduld :D

LG,
Kanne


KanneM - Mi 21.03.12 20:52

Ok, neues Problem...
Zitat:
...


EDIT: Hat sich erledigt, habs selbst hinbekommen ;)

Lg


Ralf Jansen - Mi 21.03.12 21:18

Einfach mal richtig den Exceptiontext lesen da stehts im Klartext.