Announcement

Collapse
No announcement yet.

Scripting-Fähigkeit unter .NET

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Scripting-Fähigkeit unter .NET

    So, erstmal hallo... bin neu hier, aber ich programmier schon ne ganze Weile. Ich hab ein kleines Problem, aber kann vielleicht hier und da auch mal helfen... mal kucken

    So, zu meiner Frage: ich würde gerne einige meiner Programme skript-fähig machen, d.h. dass man bei bestimmten Sachen "Makros" hinterlegen kann. Ich hab mir ein paar Techniken angesehen, aber funktionieren tut keine so richtig Vielleicht kann mir von euch jemand die Erleuchtung geben...

    CodeDomProvider:
    Das wäre die aktuelle Technik. Beliebiger Code wird zur Laufzeit kompiliert (als Datei oder direkt in den Speicher) und kann auch direkt ausgeführt werden. Nachteile: angeblich ist es relativ langsam, .NET lässt die kompilierten Assemblies im Speicher und räumt hinterher nicht sauber auf und - was das schlimmste ist - ich habe quasi ein neues Programm erzeugt, das im Speicher liegt - dieses Programm kann also auch nicht auf meine bestehenden Klassen aus dem "Erzeuger-Programm" zugreifen Zumindest ist hierüber nichts zu finden...

    VSA:
    Das wäre die Technik, die mit dem .NET-Framework 1.0 eingeführt wurde, aber ist leider äußerst umständlich und kompliziert. Noch dazu ist das Interface "IVsaSite" schon länger mit Visual Studio 2005 bzw. dem .NET-Framework 2.0 als "deprecated" gekennzeichnet und es steht explizit mit dran, dass es keinen Nachfolger geben wird - ich vermute aber mal dass der CodeDomProvider doch mehr oder weniger als Nachfolger bezeichnet werden kann... aber ich mag keine Sachen die "deprecated" sind...

    MSScripting:
    Ein ewig altes Verfahren, dass noch aus den alten Zeiten ohne .NET stammt und das ist leider auch der Haken daran. Es funktioniert super-einfach, ist noch dazu sau-schnell und mit der addObject-Methode kann man auch ganz easy Objekte aus dem eigenen Code dem Script bereitstellen - nur eben nichts, was auch nur im entferntesten mit .NET zu tun hat: nicht einmal eigene, selbstprogrammierte Klassen funktionieren und es kommt immer zu einer System.InvalidCastException (selbst bei einem explicit cast) - einzige Lösung, die ich finden konnte wäre für jedes einzelne Objekt in meinem Code eine COM-Wrapper-Klasse zu bauen (muss aber leider sagen, dass das meinen Horizont übersteigt)

    So, sorry, dass es leider so viel Text wurde... vielleicht kann mir ja jemand nen guten Rat geben... ich möchte einfach nur eine Makro-Fähigkeit für meine Programme - früher ging's total simpel, heutzutage nicht mehr?! Ich bin am Verzweifeln... wie kann ich das am Besten realisieren?

  • #2
    Hallo,

    bei diesem Thema hängt es davon ab, wo die Scripts hinterlegt werden sollen. Zum einen steht mit der Windows PowerShell ein leistungsfähiger (und mit .NET erweiterbarer) Weg zur Verfügung, wenn die Scripts primär auf der Kommandozeile ausgeführt werden sollen.

    Ansonsten sind auf http://www.codeproject.com/ meherer Script-Implementierungen zu finden, wie zum Beispiel "C#Script".

    Comment


    • #3
      Hi, danke für die Antwort Ich hatte mir gedacht, dass ich für bestimmte Funktionen meiner Programme einfach einen Register "Makro" in die Eigenschaften-Seiten baue, wo ein einfaches Textfeld mit Code gefüllt werden kann (ideal wäre natürlich VB, weil's relativ einfach ist auch für weniger erfahrene Anwender)... ich möchte damit einfach nur eine Möglichkeit schaffen, dem Anwender etwas Spielraum zu lassen, indem ich die Möglichkeit gebe Teile meines Programms beliebig zu steuern.

      Mal ein einfaches Beispiel:
      Ich hab 3 Text-Felder: txtMenge, txtPreis und txtGesamt

      Dem Anwender möchte ich nun die Möglichkeit geben ein solches Makro zu bauen:
      Code:
      if (CInt(txtMenge.Text) <> 0) and (CInt(txtPreis.Text) <> 0) then
          txtGesamt.Text = CInt(txtMenge.Text) * CInt(txtPreis.Text)
          txtGesamt.enabled = false
          MsgBox "Der Gesamtpreis wurde automatisch berechnet!"
      end if
      Die Powershell hilft mir in diesem Fall relativ wenig leider Und auch auf CodeProject hab ich schon rumgesucht, aber so richtig was passendes konnte ich nicht finden... mal nochmal weitersuchen... dieses C#Script werd ich mir auch mal ankucken... erhöht halt die Anforderungen an den Anwender ein klein wenig, aber vielleicht ja besser als garnichts
      Zuletzt editiert von ENNEMMEE; 21.01.2008, 10:10.

      Comment


      • #4
        So, ich hab mir jetzt mal C#Script (bzw. cs-script) angesehen, da mir in der Doku das hier ganz vielversprechend ausgesehen hat: http://www.members.optusnet.com.au/~...processor.html

        Nur leider besteht hier das gleiche Problem, wie beim CodeDomProvider von .NET: ich kann nicht auf bereits implementierte Komponenten zugreifen

        Wenn man dieses Tutorial von cs-script mal als Beispiel nimmt... ich würde gerne
        Code:
        buttonCompare.Text = "Bla";
        vor dem return einfügen - und genau das klappt nicht, weil es sich dabei - so wie ich das sehe - um eine eigenständige Assembly handelt die da erzeugt wird. Und genau das ist der Knackpunkt... in der alten MSScriptControl-Klasse konnte man direkt auf Schaltflächen, Textboxen, usw. zugreifen - aber jetzt mit .NET geht diese Klasse nicht mehr und Alternativen kann ich keine finden...

        cs-script ist leider das einzige, was ich neues bezüglich Scripting auf codeproject gefunden hab... die anderen Sachen hatte ich schon durchprobiert... irgendwie ziemlich schade, dass es sowas wie MSScriptControl wohl nicht mehr für .NET gibt - es war einfach, simpel umzusetzen und noch dazu sehr schnell...
        Zuletzt editiert von ENNEMMEE; 21.01.2008, 15:34.

        Comment


        • #5
          Hallo ENNEMMEE,

          (ENNEMMEE schöner Name)

          Originally posted by ENNEMMEE View Post
          Nur leider besteht hier das gleiche Problem, wie beim CodeDomProvider von .NET: ich kann nicht auf bereits implementierte Komponenten zugreifen
          doch das geht schon mit dem CodeDOM.

          Ich habe mal ein kleines Projekt angehängt, in dem das demonstriert wird. Sorry ist nicht dokumentiert, da mir die Zeit fehlt. Ich habe einfach aus einer bestehenden App ein bisschen was zusammen geklickt. Übrigens den Zugriff auf die mainForm aus dem Script würde ich so niemals machen, aber das ging jetzt am Schnellsten und zeigt die Vorgehensweise.

          HTH
          Peter
          Attached Files

          Comment


          • #6
            Hi Peter,

            sorry, das mit dem Namen ist Gewohnheit, weil ich mich überall so nenne... mach unter diesem Namen auch Musik und hat sich irgendwie so eingebürgert, wenn ich mich irgendwo anmelde Ich heisse Bruno - damit man sich auch mal ordentlich vorgestellt hat

            Vielen vielen Dank für den Code, werd ich mir heute abend nach der Arbeit gleich mal zu Gemüte führen... ich denk mal ich werd mich auch ohne Doku durchwursteln

            Auf sowas wie das Hauptformular oder direkt auf die Komponenten werde ich sowieso nicht zugreifen lassen, sondern werde jedes Objekt nochmal in eine Klasse kapseln, denn manche Sachen muss man dann ja doch nicht zum Manipulieren bereitstellen

            Herzlichen Dank auf jeden Fall nochmal!

            MfG, Bruno

            Comment


            • #7
              Top! Also dass gleich ein so gut verständliches Beispiel kommt hatte ich nicht erwartet! Funktioniert wunderbar und ich hab auch schon gesehen woran's gescheitert ist bei mir: die fehlende Referenz auf die "Host-Assembly" (ich nenn's jetzt einfach mal so)... und auf so ne Idee wie die DataAssembly bin ich auch nicht gekommen...
              Echt ein klasse Beispiel!

              Nur eins ist da noch: der Punkt, dass .NET den Speicher nicht mehr freigibt (kann man an diesem Beispiel auch schön mitverfolgen, wie der Speicherverbrauch des Prozesses wächst) solange bis der Prozess beendet wird... hast du da eventuell auch Erfahrungen? Du scheinst dich da ja doch recht gut auszukennen in diesem Bereich... würde es vielleicht sogar reichen die Kompilierung und Ausführung in einen eigenen Thread zu packen, der danach wieder sauber entfernt wird? Müsste ich glatt mal probieren ob .NET dann zumindest die Ressourcen wieder freigibt, die der Thread verbraucht hat (also auch den Speicher, den die Assembly verbraucht hat)... ich hab ja die Befürchtung, dass es nicht reicht, aber vielleicht ja doch... ich google mal

              *EDIT*:
              So, beim ersten Googlen gleich die Ernüchterung: ne "echte" Lösung gibt's wohl angeblich nicht... erfordert wohl nen Recode einer der zentralen Komponenten des Frameworks... aber es gibt Techniken, das Ganze auf ein erträgliches Maß zu reduzieren:
              KLICK MICH! (der Absatz "What are the Hurdles to Be Surmounted" ist hier recht interessant)
              Falls noch jemand anderes vor dem gleichen Problem steht

              Ich find die Idee mit dem Assembly-Cache eigentlich recht schön: warum sollte man etwas 100 mal kompilieren und den Speicher damit zumüllen, wenn man's ebenso gut wiederverwenden kann bringt ja auch Performance...
              Zuletzt editiert von ENNEMMEE; 22.01.2008, 17:42.

              Comment


              • #8
                Hallo Bruno,

                ja der Speicherverbrauch ist sicherlich ein Problem.

                Aber der Artikel, den Du zitierst, zeigt ja eine Lösung auf: AppDomains.

                Zur Verwendung von AppDomains gibt es eine Fülle von weiteren Informationen im Internet. U.a.

                http://weblogs.asp.net/ralfw/archive...6/23/9148.aspx

                Klar ist mehr Codingaufwand, aber einen großen Vorteil haben die Dinger: Sicherheit, siehe u.a. hier

                http://it-republik.de/dotnet/artikel...-weg-0390.html

                Stichworte:CAS, Sandbox, PermissionSet. Damit kannst Du zum Beispiel dem in der AppDomain ausgeführten Code verbieten, FileOperationen auszuführen. Gerade bei Scripting sehr interessant.

                Dafür habe ich leider kein Beispiel, dass ich so einfach "rausoperieren" kann, aber ich denke, Du bekommst das auch so hin.

                Viel Erfolg
                Peter

                P.S.
                ..: warum sollte man etwas 100 mal kompilieren und den Speicher damit zumüllen, wenn man's ebenso gut wiederverwenden kann bringt ja auch Performance...
                Klar, wenn es nicht zuviele verschiedene Scripte sind und einzelne mehrfach verwendet werden, kann man natürlich den compilierten Code "aufheben" und wieder verwenden.

                P.P.S.
                Eine eigener Thread löst das Speicherproblem nicht.

                Comment


                • #9
                  Ah super danke Also jetzt nach dem ersten Durchlesen von http://weblogs.asp.net/ralfw/archive...6/23/9148.aspx bin ich zwar noch etwas verwirrt was das "nicht verwenden von Typen aus der Haupt-Assembly in der Proxyklasse" angeht, aber das krieg ich schon noch hin... verstehen möcht ich's zumindest auf jeden Fall mal und der Sicherheitsaspekt spielt ja auch mit rein...

                  Ich hab hier z.B. eine recht große Applikation programmiert, wo ich an vielen Stellen viele unterschiedliche Skripte bzw. Makros implementieren möchte... wenn ich an die Performance denke, gefällt mir der Ansatz mit dem Assembly-Cache recht gut... aber wenn ich an den Speicher denke, gefällt mir das Entladen über die AppDomain-Methode ganz gut (worüber man ja, wie du schon sagtest, auch schön Sicherheitsrichtlinien vergeben kann) - ich denke ich werd dann mal auf eine Mischform hinarbeiten: ein Assembly-Cache mit einstellbarer Verfallszeit (da bin ich wieder paar Wochenenden beschäftigt )

                  Ich glaub mittlerweile ist der Thread schon ne recht interessante Linksammlung geworden - mit dem Beispielcode als i-Tüpfelchen
                  Zuletzt editiert von ENNEMMEE; 23.01.2008, 12:25.

                  Comment


                  • #10
                    Vielleicht kannst Du hier ja mal Deine Lösung kurz skizzieren, wenn sie fertig ist.

                    Comment


                    • #11
                      Klar mach ich...

                      Comment


                      • #12
                        So, ich hab jetzt ne Weile rumgespielt und mich eingelesen und bin auch super vorangekommen... nur leider bin ich jetzt auf ein Problem gestoßen und finde den Fehler einfach nicht
                        Ich habe das Projekt einfach mal angehängt (Peter, ich hoff du nimmst es mir nicht übel, dass ich da jetzt einfach deinen Code teilweise hineinkopiert habe - da werde ich auch noch einiges an Änderungen vornehmen)...

                        Erklärung des Projekts:
                        AssemblyCache benutzt ein Dictionary zur Verwaltung der Assemblies: als Key wird einfach der Hash des Code-Strings verwendet - als Value werden Instanzen der Klasse AssemblyCacheEntry benutzt.
                        AssemblyCacheEntry erzeugt die AppDomain und darin eine Instanz der Klasse AssemblyProxy, die letztendlich dann die Compilierung des Codes vornimmt.

                        So, zum Problem:
                        Ich habe im Teilprojekt "test" einfach mal versucht, das ein wenig zu testen mit zwei Funktionen: RunAndClearAtOnce() und RunAndClearLater() - so weit, so gut
                        RunAndClearAtOnce() erzeugt eine Assembly und entlädt danach sofort wieder die AppDomain - das Ganze 50 mal.
                        RunAndClearLater() erzeugt 50 Assemblies und entlädt danach in einem zweiten Schritt die AppDomains.
                        Durch Auskommentieren jeweils einer der Funktionen kann man da ein wenig testen.

                        Was mir aufgefallen ist:
                        * wenn ich 50 Assemblies erzeuge hat das Programm eine Speicherbelegung von etwas über 37 MB
                        * wenn ich diese 50 AppDomains entlade fällt die Speicherbelegung auf ca. 31 MB
                        * wenn ich dagegen RunAndClearAtOnce() ausführe fällt die Speicherbelegung auf ca. 20 MB

                        Das sind über 10 MB Unterschied, die mich doch ein wenig grübeln lassen, denn eigentlich passiert vom Prinzip her in beiden Funktionen das gleiche (abgesehen davon, dass RunAndClearLater() einen kleinen Umweg geht, aber das macht lange keine 10 MB aus)...

                        Irgendwo hab ich da nen groben Denkfehler drin hab ich das Gefühl aber zumindest als Grundstruktur ist es mal läuffähig... darauf kann man natürlich aufbauen

                        So, das war jetzt ein kleiner Zwischenbericht - und vielleicht sieht ja ein Außenstehender mehr als ich und sieht den Fehler
                        Attached Files

                        Comment


                        • #13
                          Ok, nach einigem Rumprobieren die Erleuchtung: der Garbage Collector scheint einfach nicht immer sofort aufzuräumen... das ständige Anlegen und Entladen der AppDomains scheint ihn zu animieren was zu tun, das einmalige wohl nicht so sehr... hab einfach mal 6 mal die RunAndClearLater() laufen lassen und landete bei ca. 40 MB Speichernutzung im Taskmanager... als die RunAndClearLater() danach nochmal lief, viel der Speicherverbrauch wieder auf unter 30 MB... eigentlich recht eindeutig... mal nach nem Weg suchen den Garbage Collector zu animieren... interessiert mich jetzt ob meine Vermutung richtig ist

                          *EDIT*
                          Ok, es ist definitiv der Garbage Collector... womit ich mir den Sonntag abend um die Ohren schlag ist schon unglaublich
                          Zuletzt editiert von ENNEMMEE; 28.01.2008, 00:17.

                          Comment


                          • #14
                            Hallo Bruno,

                            der Garbage Collector scheint einfach nicht immer sofort aufzuräumen
                            Stimmt. Warum sollte er auch? Wenn noch Speicher vorhanden ist, kann er sich doch Zeit lassen. Problem dabei sind natürlich Referenzen auf non managed Resourcen, wie files, DB-Connections etc. Da muss der Programmierer halt dafür sorgen, dass die Dinger rechtzeitig frei gegeben werden. (using)

                            Dass man mit GC.Collect, den Garbage Collector zum Aufräumen zwingen kann, weißt Du sicherlich?

                            Einen schönen Artikel zum GC gibt es hier:

                            http://msdn.microsoft.com/msdnmag/is...lt.aspx?loc=us

                            Peter, ich hoff du nimmst es mir nicht übel, dass ich da jetzt einfach deinen Code teilweise hineinkopiert habe
                            Kein Problem.

                            Gruß
                            Peter

                            Comment


                            • #15
                              Jap, hab ich gestern noch getestet und dann hat sich das alles auch ziemlich rasch aufgeklärt Nur im ersten Moment hatte ich die Befürchtung, dass ich ein Memory Leak fabriziert hatte...

                              Comment

                              Working...
                              X