Forum: PC-Programmierung C# Zur Laufzeit Modul kompilieren und darauf zugreifen


von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

Also,

zur Laufzeit erstelle ich ein cs file, welches eine simple klasse 
enthält:
1
using System;
2
namespace WindowsFormsApplication1
3
{    
4
  class bla  
5
  {
6
    public int xyz;    
7
    public bla(){}
8
                publis void setbla(int val){xyz = val;}
9
         }
10
}

Ich muss nun auf diese Klasse zugreifen können. Also kompiliere ich 
diese und erstelle eine DLL. Das sollte doch prinzipiell machbar sein, 
mit der obigen Klasse oder?

Dazu habe ich dieses Beispiel verwendet und anstatt einer EXE, eine DLL 
daraus gemacht http:
http://msdn.microsoft.com/en-us/library/system.codedom.compiler.compilerparameters.generateexecutable.aspx
Hat auch soweit geklappt.

Wie binde ich diese nun zur Laufzeit ein? Muss ich dazu eine neue Domäne 
anlegen? Weil wenn ich mir das so anschaue, verstehe ich nur Bahnhof!

Kann mir jemand weiterhelfen?

von Christian R. (mrrotzi)


Lesenswert?

Wenn du das Assembly als DLL vorliegen hast, kannst mit Reflection alles 
damit tun!
1
var assembly = System.Reflection.Assembly.LoadFrom(@"c:\assembly.dll");

aus dem assembly ladest du die Typendefinitionen:
1
var type = assembly.GetType("Klassenname");

und mit Activator kannst du dann eine Instanz der Klasse erzeugen
1
object instance = Activator.CreateInstance(type);


Warum aber überhaupt on the fly assemblies erzeugen??? ? ?? ... ?

Grüße,
Christian

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

Das ist so eine Sache. Rufe zur Laufzeit eine exe auf, die ein file 
parst und daraus eben diese Klasse erstellt. Wüsste nicht wie ich es 
anders lösen könnte. Aber den c code, aus dem die exe erstellt wurde, zu 
portieren, würde etwas länger dauern, deswegen dieser weg.
ok werde es mal testen.

von __tom (Gast)


Lesenswert?

Peterle Anonym schrieb:
> Muss ich dazu eine neue Domäne
> anlegen?

nur wenn du das assembly wieder entladen möchtest, es gibt (zumindest 
bis .net35) keinen mechanismus ein einmal geladenes assemlby wieder zu 
entladen. das zerstören einer appdomain ginge aber.

von Markus V. (valvestino)


Lesenswert?

Hi Peterle,

.net hat ein mächtiges Feature: Reflection. Es bietet die Lösung für 
dein(e) Problem(e). Du müßtest Dir zunächst eine Instanz des Compilers 
über System.CodeDOM.CodeDOMProvider.CreateCompiler("CSharp") und über 
deren Methode CompileSourceFromFile (...FromSource,...FromDOM) ein 
Assembly-Objekt erzeugen. Über Reflection kannst Du dann Objekte von 
Klassen aus Deiner Assembly instanziieren und Methoden derselben 
aufrufen.

Eine andere Möglichkeit wäre, die Methoden der Objekte aus der zur 
Laufzeit erzeugten Assembly über ein Interface anzusprechen, das 
natürlich zur Compile-Zeit Deines Hauptprogramms vorhanden sein muß. 
Dann sparst Du Dir den Methodenaufruf über Reflection.

Du solltest auf jeden Fall mal die MSDN-Doku zum Thema konsultieren, da 
es leider einige Parameter bei den beteiligten Klassen gibt, die passend 
versorgt sein wollen.

Gruß
Markus

von peterle (Gast)


Lesenswert?

Christian R. schrieb:
> Wenn du das Assembly als DLL vorliegen hast, kannst mit Reflection alles
> damit tun!
> var assembly = System.Reflection.Assembly.LoadFrom(@"c:\assembly.dll");
>
> aus dem assembly ladest du die Typendefinitionen:
> var type = assembly.GetType("Klassenname");
>
> und mit Activator kannst du dann eine Instanz der Klasse erzeugen
> object instance = Activator.CreateInstance(type);
>
>
> Warum aber überhaupt on the fly assemblies erzeugen??? ? ?? ... ?
>
> Grüße,
> Christian

Wie greife ich dann auf die Methode der klasse zu, denn zur 
Kompilierzeit ist diese nicht bekannt und der Compiler meckert:
1
...
2
instance.test(33);

test ist nicht bekannt in diesem kontext!

von Klaus W. (mfgkw)


Lesenswert?

Peterle Anonym schrieb:
> Aber den c code,

C? Ich dachte C#?

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

Markus Volz schrieb:
> Hi Peterle,
>
> .net hat ein mächtiges Feature: Reflection. Es bietet die Lösung für
> dein(e) Problem(e). Du müßtest Dir zunächst eine Instanz des Compilers
> über System.CodeDOM.CodeDOMProvider.CreateCompiler("CSharp") und über
> deren Methode CompileSourceFromFile (...FromSource,...FromDOM) ein
> Assembly-Objekt erzeugen. Über Reflection kannst Du dann Objekte von
> Klassen aus Deiner Assembly instanziieren und Methoden derselben
> aufrufen.
>
> Eine andere Möglichkeit wäre, die Methoden der Objekte aus der zur
> Laufzeit erzeugten Assembly über ein Interface anzusprechen, das
> natürlich zur Compile-Zeit Deines Hauptprogramms vorhanden sein muß.
> Dann sparst Du Dir den Methodenaufruf über Reflection.
>
> Du solltest auf jeden Fall mal die MSDN-Doku zum Thema konsultieren, da
> es leider einige Parameter bei den beteiligten Klassen gibt, die passend
> versorgt sein wollen.
>
> Gruß
> Markus

hi,

ja also kompiliert habe ich das ganze zur laufzeit schon und eine dll 
wurde auch erstellt. das hab ich folgendermaßen gemacht:
1
FileInfo sourceFile = new FileInfo(sourceName);
2
CodeDomProvider provider = null;
3
provider = CodeDomProvider.CreateProvider("CSharp");
4
CompilerParameters cp = new CompilerParameters();
5
cp.GenerateExecutable = false;
6
//cp.ReferencedAssemblies.Add("System.Core.dll");
7
// Specify the assembly file name to generate.
8
String exeName = String.Format(@"{0}\{1}.dll",
9
                    System.Environment.CurrentDirectory,
10
                    sourceFile.Name.Replace(".", "_"));
11
cp.OutputAssembly = exeName;
12
13
// Save the assembly as a physical file.
14
cp.GenerateInMemory = true;
15
16
// Set whether to treat all warnings as errors.
17
cp.TreatWarningsAsErrors = false;
18
19
// Invoke compilation of the source file.
20
CompilerResults cr = provider.CompileAssemblyFromFile(cp,
21
    sourceName);


sollte so passen. wobei ich nicht bei allen parametern sicher bin

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

Klaus Wachtler schrieb:
> Peterle Anonym schrieb:
>> Aber den c code,
>
> C? Ich dachte C#?

nein. ich rufe aus C# eine exe auf. DIESE wurde in C erstellt, sonst 
wäre es ja halb so wild ;)

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

Christian R. schrieb:
> Wenn du das Assembly als DLL vorliegen hast, kannst mit Reflection alles
> damit tun!
>
>
1
> var assembly = System.Reflection.Assembly.LoadFrom(@"c:\assembly.dll");
2
>
>
> aus dem assembly ladest du die Typendefinitionen:
>
>
1
> var type = assembly.GetType("Klassenname");
2
>

wenn ich hier den klassennamen reinschreibe, liefert mir GetType null 
zurück
und:

> und mit Activator kannst du dann eine Instanz der Klasse erzeugen
>
>
1
> object instance = Activator.CreateInstance(type);
2
>

das schlägt fehl.

Ohne Parameter liefert GetType schon mal nicht null, sondern 
System.Reflection.RuntimeAssembly. Dann aber sagt er

No parameterless constructor defined for this object

stimmt vll was mit meiner DLL nicht, bzw der darin enthaltenen klasse?

Wenn ich mir GetType(string)-Methode anschaue, steht in der msdn, dass 
dieser ein  AssemblyQualifiedName erwartet... oh man

von Markus V. (valvestino)


Lesenswert?

Na ja, Du musst beim Klassenname zumindest mal den Namespace mit 
angeben, sonst wird das nichts, gemäß Deinem obigen Beispiel: 
WindowsFormsApplication1.bla.

Wieso versuchst Du eigentlich, die Assembly als Datei von der Platte zu 
laden? Das geht natürlich, aber Du hast sie im CompilerResult-Objekt 
bereits als Objekt vorliegen und kannst direkt damit arbeiten. Zum 
Compilieren musst Du den Sourcecode auch nicht in eine Datei schreiben. 
Du kannst auch ein String-Objekt compilieren.

Aus Deinen Infos oben geht nicht so klar hervor, ob die DLL nun eine 
Windows-DLL oder eine .net-Assembly ist. Die haben zwar beide die selbe 
Dateinamenserweiterung (.dll), haben aber sonst nichts gemeinsam.

Eine Windows-DLL kannst Du mittels P-Invoke laden und Funktionen daraus 
ansprechen. Für .net-Assemblies ist Reflection vorgesehen.

Gruß
Markus

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

wie hier beschrieben:
Beitrag "Re: C# Zur Laufzeit Modul kompilieren und darauf zugreifen"

handelt es sich um ein assembly.

>aber Du hast sie im CompilerResult-Objekt
>bereits als Objekt vorliegen und kannst direkt damit arbeiten

meinst du ich kann direkt mit dieser Referenz arbeiten:
1
CompilerResults cr = provider.CompileAssemblyFromFile(cp,
2
    sourceName);


>Compilieren musst Du den Sourcecode auch nicht in eine Datei schreiben.
>Du kannst auch ein String-Objekt compilieren.

naja die zu kompilierende Datei wird ja durch ein externes Programm zur 
Laufzeit erzeugt. wüsste nicht wie ich da einen string übergeben 
könnte?!

von Markus V. (valvestino)


Lesenswert?

Ok, da der Sourcecode extern erzeugt wird, ist CompileAssemblyFromFile 
natürlich die bessere Wahl.

Die Objekte des Typs CompilerResults haben ein Property CompiledAssembly 
vom Typ Assembly. Von diesem Assembly-Objekt kannst Du dann wieder die 
Methode CreateInstance aufrufen, um Instanzen von Deiner Klasse zu 
erhalten. Der Klassenname umfasst, wie bereits oben erwähnt, neben dem 
Klassennamen natürlich auch den Namespace!

Gruß
Markus

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

Markus Volz schrieb:
> Ok, da der Sourcecode extern erzeugt wird, ist CompileAssemblyFromFile
> natürlich die bessere Wahl.
>
> Die Objekte des Typs CompilerResults haben ein Property CompiledAssembly
> vom Typ Assembly. Von diesem Assembly-Objekt kannst Du dann wieder die
> Methode CreateInstance aufrufen, um Instanzen von Deiner Klasse zu
> erhalten. Der Klassenname umfasst, wie bereits oben erwähnt, neben dem
> Klassennamen natürlich auch den Namespace!
>
> Gruß
> Markus

ahh, cool, mit dem namespace klappt es, wie trivial...

Aber wenn ich jetzt einen member (methode, variable) aufrufen will, 
meckert er, da er sie ja zur compile-zeit nicht kennt!!
1
int x = instance.test;

von Christian R. (mrrotzi)


Lesenswert?

Das geht auch über Reflection:
1
var method = type.GetMethod("test");
2
method.Invoke(instance, new object[] { });


Grüße,
Christian

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

ahhh danke, klappt soweit.

Kann es sein, dass ich für jede methode die ich aufrufen will eine neues 
instance erzeugen muss via CreateInstance?
1
/// mit parameter
2
            object instance = Activator.CreateInstance(type);
3
            var method = type.GetMethod("abc");
4
            method.Invoke(instance, new object[] {7});
5
// mit rückgabewert
6
            object instance1 = Activator.CreateInstance(type);
7
            var method1 = type.GetMethod("add");
8
            var b = method1.Invoke(instance1, new object[] {});

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Peterle Anonym schrieb:
> ahhh danke, klappt soweit.
>
> Kann es sein, dass ich für jede methode die ich aufrufen will eine neues
> instance erzeugen muss via CreateInstance?
1) Nein wie kommst du darauf?
2) Wie wäre es eigentlich dem Rat mal zu folgen sich in das Themengebiet 
einzulesen? Alternativ bitte den vollständigen Code liefern das wir dir 
das hier jetzt nicht Stück für Stück zusammenprogrammieren müssen...

von Peterle A. (Firma: keine) (wanderameise)


Lesenswert?

hat sich geklärt, es klappt alles soweit wie gewünscht.
Danke Leute!!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.