NativeAutoItObjectEx (Objekte in AutoIt)

  • Guten Tag,

    Diese UDF ermöglicht das (relativ) einfache Erzeugen von COM-Objekten in AutoIt. Dabei wird die Syntax von Sprachen die OOP nativ unterstützen teilweise imitiert (wozu das Rad neu erfinden), sodass man sowohl als Einsteiger als auch als alter Hase schnell zurechtfindet.

    Wie funktioniert das ganze?

    Das ist eine gute Frage , es gibt die wunderbare BuiltIn Funktion ObjCreateInterface die so umständlich zu handhaben ist, dass niemand viel damit angefangen hat und niemals ans Licht kam was man damit alles anstellen kann. Die hier erzeugbaren Objekte bestehen aus einer DllStruct die Objektvariablen und Callbacks zu AutoIt-Funktionen beinhaltet. Da es sehr viel Aufwand ist diese Struct jedes Mal manuell zu erstellen nimmt einem die UDF diesen Part der Arbeit ab. Die AutoIt-Funktionen die später als Objekt-Methoden dienen muss man natürlich trotzdem von Hand schreiben.

    Was geht:

    - Objektvariablen MIT Datentyp (kein Variant, genauso wie die Datentypen in DllStructs)

    - Methoden mit Alias, man kann also eine Funktion schreiben die zwar __IchBin_EinKomplizierterName_BerechnePI() heißt, vom Objekt aber als $o.PI aufgerufen werden kann (bei Methoden gilt etwas das für AutoIt-Funktionen nicht gilt, haben sie keinen Parameter kann man die Klammern weglassen)

    - Objekte in Objekten (was wäre die Welt, wenn man ein Rechteck aus 4 Zahlen und nicht aus 2 Punkten definieren müsste)

    - Zugriff auf Objektvariablen und Methoden von außerhalb und innerhalb von Objekten (jetzt auch ohne Maps, keine BETA Pflicht mehr)

    - Vieles mehr (was man eben so alles anstellen kann)

    Bekannte Bugs/Probleme/ToDo-Liste (je nachdem wie man es nennen will):

    - Objektvariablen sind alle "privat", man kann nicht via $o.x auf die Variable x aus $o zugreifen. Man kann aber die Struct die die Variablen enthält auch von außerhalb erreichen und so die Objektvariablen lesen und schreiben... Das ganze lässt sich wrappen, aber (zumindest nach meinem Kenntnissstand) nicht trivial als $o.x.

    - ByRef funktioniert in Objektmethoden nicht. (DllCallbackRegister will sowas nicht... ob man das wrappen kann muss noch erörtert werden, sieht aber schwierig aus)

    - Arrays können nicht an Methoden übergeben werden. (DllCallbackRegister will sowas nicht... ob man das... siehe oben)

    Edit: 18.Mär.19

    Rebrand:

    - Die UDF heißt jetzt NAUO (Native AutoItObject) um Namenskollisionen mit der AutoItObject UDF von ProgAndy zu verhindern. (Native weil keine DLLs oder andere Zusatzsoftware benötigt wird)

    Added:

    - Method(string, [string, [string, [string, [string]]]]) kann jetzt die Parameter auch im ersten String haben, z.B. Method('Math_Sqrt[Sqrt](double, double)') = Method('Math_Sqrt[Sqrt]', 'double', 'double')

    - Var(Obj, string) -> Read Variable called "string"

    - Var(Obj, string, x) -> Write x into Variable called "string"

    BugFixes:

    - Objekte ohne Variablen führen nun nicht mehr zum Absturz.

    Script Breaking Changes:

    - New('String', $this) liefert jetzt automatisch New('String', $this).Ptr, in alten Skripten muss also das .Ptr entfernt werden (verschachtelung von Objekten, Siehe Beispielskript 2).


    Ladet den Anhang herunter und probiert die 4 Beispiele mal aus :)

    Anregungen oder Wünsche oder Bugmeldungen sind gerne gesehen, nur so kann die UDF weiterentwickelt werden. Ich bin aber ein fauler Mensch und es kann Monate oder Jahre dauern bis ich an der UDF weiterarbeite :)

    Alter inhalt

    Seit AutoIt die Funktion ObjCreateInterface besitzt ist es möglich mit kleinem Aufwand "echte" Objekte zu erzeugen. In den letzten Tagen ist daraus eine kleine UDF entstanden. Sie befindet sich im Aufbau und bietet aktuell deutlich weniger als Konkurrenzprodukte welche auf externe Dateien wie DLLs, oder nicht Standard UDFs zurückgreifen. Sie soll auch keine echte Konkurrenz darstellen, sondern eine einfache und kleine Lösung bieten.

    Für Interessierte:
    Es steht jedem frei Features zu wünschen, Bugs zu melden oder Beispiele zu OOP und spezifischen Problemen zu verfassen (bitte im Thread hier posten). Dabei sollte darauf geachtet werden, dass die UDF möglichst unabhängig bleibt (also keine DLLs, keine nicht Standard UDFs und StandardUDFs nur im Notfall). Optimal wäre es natürlich, wenn man zu Problemen die man findet eine geeignete Lösung parat hat, falls dem nicht so ist kommt der Bug auf die Liste, gleiches gilt für Features.

    Manche Funktionen der UDF setzen die BETA voraus, so werden Methoden in Methoden Beispielsweise als Map gehandelt (sodass man möglichst einfach an die Methoden kommt).

    Wir freuen uns über jeden der etwas zu der UDF sagt, oder sie verbessern/benutzen möchte.


    Update 05.01.2019:

    In letzter Zeit kamen mir ein paar Ideen wie man die "umständliche" Verwendung der UDF ein wenig auslagern kann, sodass die Bedienung stark vereinfacht wird (man kann Objekte jetzt "ähnlich" (aber nicht gleich) wie in anderen Sprachen deklarieren. Außerdem sind ein paar kleine neue Features/Fixes hinzugekommen. Das Ganze ist noch nicht gut kommentiert und UDF-Header mit Erklärungen gibt es auch erst später, aber man kann schonmal reinschauen.

    Hinweis: Die Klassen müssen immer VOR der Verwendung deklariert werden, da AutoIt keine Keywords "Class" oder "Method" besitzt werden diese durch Funktionen imitiert die ausgeführt werden müssen bevor es möglich ist Instanzen einer Klasse zu erzeugen. Klassen also am besten Themenbedingt in einzelne Dateien verpacken und diese zu Beginn includen, dann kann nichts schiefgehen.


    Die UDF + Beispiel gibts im Anhang.

    Offene Fragen:

    - Wie speichert man Objekte in Objektvariablen? (Diese Variablen sind nur primitive Datentypen, kann man ggf. via oPtr Objekte verschachteln?)

    - Kann es möglich sein Klassen innerhalb von Funktionen/Anderen Anweisungen zu deklarieren? (Aktuell MÜSSEN Klassen toplevel deklariert werden, da das Func-Keyword für Methoden benutzt wird was es leider ausschließt woanders ausgeführt zu werden....)

    Wie immer gilt: Wer Vorschläge (Antworten) hat, her damit (auch wenn ich 3 Jahre brauche bis ich darauf zurück komme :P)

    Gelöste Fragen:

    # Objekte in Objekten # 06.01.2019

    Erstmal wird ein Objekt automatisch gelöscht, sobald die letzte AutoIt-Interne Referenz verlorengeht. Also muss dafür gesorgt werden, dass Objekte die man nur via ptr speichern will dennoch erhalten bleiben. Zum Glück haben Objekte einen einzigartigen Ort im RAM (ptr) den man super als Hashkey benutzen kann, sodass jedes Objekt bei der Erzeugung in einem Hashtable hinterlegt wird. Möchte man nun via ptr darauf zugreifen muss man nur im HashTable wühlen und das Objekt wieder herausholen. Das geht von überall aus, sodass man Objekte in Objekten ohne Probleme als Variablentyp ptr speichern kann. Damit AutoIt korrekt löscht, wenn z.B. ein Objekt das Objekte enthält gelöscht wird muss der Destruktor benutzt werden, in diesem werden alle child Objekte abgearbeitet (aus dem Hashtable löschen). Dabei taucht ein neues Problem auf: Was ist, wenn ein Objekt in mehreren anderen Objekten enthalten ist? Dann wird es im Hashtable nur 1x hinterlegt und liefert eine ungültige Referenz sobald es gelöscht wird, obwohl es in anderen Objekten ggf. noch vorkommt. Abhilfe könnte hier ein Hash aus (ptr Child & ptr Parent) liefern, dann wird jedes Objekt so oft in den HT gelegt wie es referenziert wird. Da das ganze nicht so rund läuft wie ich es gerne hätte und vorallem noch NICHT automatisiert ist (man muss child Objekte explizit löschen, das lässt sich sicherlich wrappen, sodass man sich als Benutzer nicht darum kümmern muss) gibts noch kein Update der Dateien an dieser Stelle.

    Update 06.01.2019:

    Es ist nun möglich Objekte innerhalb von Objekten zu erzeugen. Die Ganze Sache ist noch etwas holprig (und nicht vollständig gewrappt) funktioniert aber schonmal. Daher gibt es ein Beispiel02 welches ein Rect durch 2xPoint definiert.

    lg
    M

  • Irgendwie muss der erste Parameter bei Methoden weg. Eine Instanz muss sich (irgendwie) von alleine abgrenzen, ohne dass der User noch extra daran denken muss.

    Dann kann ich mich endlich von VB6 verabschieden :D (bis jetzt schreibe ich Klassen in VB und nutze diese in AutoIt).

  • Wie soll die UDF ausgelegt werden? Die bisherige AutoItObject UDF wurde (nach meinen Recherchen) für dieArbeit mit COM Objekten geschrieben. Soll hier wirklich die OO Programmierung im Fokus sein?

  • Irgendwie muss der erste Parameter bei Methoden weg. Eine Instanz muss sich (irgendwie) von alleine abgrenzen, ohne dass der User noch extra daran denken muss.

    So wird es aber nunmal gemacht. Eine Objekt-Methode ist auch nur eine ganz normale Funktion welche noch einen Verweis auf ein konkretes Objekt übergeben bekommt (Im Gegensatz zu einer Klassenmethode).
    Es gibt Sprachen, wie VB oder C++, die verstecken dieses Vorgehen vor dem User. Andere wie z.B. Python setzen auf das Prinzip "explizit vor implizit" und machen es genauso wie hier (siehe self-Operator von Python).
    Mars hätte auch nicht wirklich viel Handhabe einen impliziten Verweisparameter umzusetzen, da es sich dabei ja um ein Sprachfeature handelt (die Sprache versteckt den Parameter in ihrer Syntax) und diese kann er per UDF ja nicht verändern.

    @Mars
    Nicht schlecht nicht schlecht.
    Vor allem, dass die Dereferenzierung so gut klappt und kein Memory-Leak auftritt. :klatschen:

  • Einige Jahre sind vergangen und an einigen Stellen habe ich mich geärgert, dass ich diese UDF hier nicht wieder angefasst habe nachdem sie doch so ein gutes Fundament für "groben Unfug" liefert. Also habe ich meine Freizeit investiert und einen Wrapper (und ein paar Features/Bugfixes) für die UDF gebastelt die es ermöglichen soetwas hier zu schrieben:

    Auch wenn die Au3 Entwickler keine Objekte wollen, wir haben sie trotzdem :D

    lg

    M

  • Mars 8. Januar 2019 um 19:18

    Hat den Titel des Themas von „AutoItObject UDF (im Bau, nativ)“ zu „AutoItObjectEx (Objekte in AutoIt)“ geändert.
  • Und direkt ein Doppelpost. Die UDF ist nun fertig. Habt Spaß damit.

    "fertig" bedeutet, dass ich davon ausgehe, dass die UDF im Kern ab jetzt Abwärtskompatibel bleibt. Skripte die heute Laufen sollten auch Morgen noch gehen.

    (Fehlermeldungen und Verbesserungsvorschläge sind gern gesehen)

    lg

    M

  • Was bedeutet in diesem Kontext "ByRef". Also was genau soll ByRef übergeben werden können? Wenn du ein Objekt selbst meinst (also eine Funktion die als Parameter byref ein Objekt bekommt) kann ich mal herumprobieren.

    Edit: Da der Kern der UDF genau der selbe ist wie vorher gebe ich aber davon aus dass das Vorhaben nicht klappt.

    Man muss auch in Objekten $a = ObjP(xyz) und dann $a.method aufrufen, weil Autoit scheinbar die referenz killt sobald der Wert von etwas zurückgegeben wurde ohne zu beachten dass er ggf. noch weiterverwendet wird. Das könnte man natürlich damit umgehen, dass man das Objekt zwar ByRef übergibt, aber eine referenz irgendwo seperat abspeichert. Das prüfe ich aber heute nicht mehr.

  • Das geht hier auch nicht. Intern wird DllCallbackRegister genutzt um an Pointer für die Funktionen zu kommen die das Objekt dann verinnerlicht. Leider ist diese Funktion nicht ByRef Kompatibel und in der AutoIt-Hilfe/bzw. dem großen Internet steht auch kein Lösungsansatz.

    Man kann zwar via 'int*' eine Dllstruct ByRef verteilen (in dem Fall wird einfach der Pointer übergeben), AutoIt-Variablen scheinen aber AutoIt-Typisch eine Temporäre Struct zu bekommen deren Inhalt nach einer Neuzuweisung ignoriert wird.

    Villeicht übersehe ich auch irgendwas. Hier mein Beispielskript:

    (Man kann das Verhalten mit Structs nachbilden, da ist ByRef kein Problem, aber ich schätze mal das weißt du und das ist nicht was du suchst)

    Edit: Falls jemand DllStructCallbackRegister mit Byref hinbekommt kann ich das in die UDF einbauen.

    Außerdem habe ich einen Bug gefunden: Man MUSS Objektvariablen deklarieren (also mindestens eine) weil die UDF sonst abstürzt. Das schaue ich mir später nochmal an. Als Fix einfach Var('int abc') reinschreiben, dann geht alles.

    Edit2: Die UDF von ProgAndy & Friends baut auf einer mitgelieferten DLL auf und hat nichts mit dieser UDF hier zu tun (die ohne alles auskommt). Die Gundidee ist im Prinzip die selbe, nur die Methode wie man es in AutoIt wrappt ist unterschiedlich.

    M

    • Offizieller Beitrag

    Hallo Mars

    2 Dinge, die ich ändern würde:

    - Du verwendest den Namen AutoItObject.au3 - denselben Namen, wie die UDF von prog@ndy & Co. Ich habe das bei mir jetzt umbenannt (Skript und include in der Ex) zu AU_Object.au3, damit ich keine Crashs bekomme, da ich auch die andere UDF verwende.

    - Das zweite ist nicht zwingend, wäre aber nett. ;)

    Dadurch, dass du ausschliesslich auf die Dll-Struct-Konvention zurückgreifst, können Strings (variabler Länge) als Variablen nur über Pointer mit Verweis auf eine Struct gespeichert werden (oder natürlich gleich mit einem char-Array).

    Hier würde ich vielleicht ermöglichen den Datentyp "str" zu verwenden (reiner Komfort!), mit einer maximalen Länge von z.B. 256 (als Konstante festlegen). Eingebunden bei dir, würde das so aussehen:

    Das wars auf den ersten Blick - ich teste mal weiter.


    EDIT:

    Hab mal ein bischen gespielt: Sehe ich das richtig, man kann auf die Properties (Var) nicht mit der Syntax Wert = obj.property zugreifen, sondern muss für jede Wertabfrage eine eigene Methode erstellen?

    Bsp.

    Wonach ich noch gesucht habe: Wie kann ich den Classennamen eines Objektes abfragen oder ist das nicht vorgesehen? Dann vielleicht Standard-Property __class immer definieren?

    Vielleicht auch ganz nützlich: New() mit einem optionalen Parameter für einen Namen ausstatten. Bedingt dann natürlich, dass eine Variable (z.B. __name) automatisch immer mit erstellt wird.

    Das waren jetzt Punkte, die ich aus der Gewohnheit als recht angenehm empfinde.

    Wenn du mit deiner UDF, die dir gesteckten Aufgaben abarbeiten kannst, ist das natürlich auch OK. Wir können ja bei Bedarf auch selber Hand anlegen. :P:thumbup:

    EDIT 2:

    wg. DllCallback: ByRef finde ich da jetzt nicht sehr aufwändig, muss man halt den Wert vor und nach dem Call covern (OK, ist kein echtes ByRef, da der Wert der Variablen über die Strukt transferiert werden muss):

    Callback ByRef
  • Die Library sieht auf den ersten Blick genial aus. =O

    Hab vor ein paar Tagen ne GDI+ Control Library angefangen und habe eigene Funktionen wie "_GdiCreateButton()" etc. "oo" gestaltet. Allerdings mit

    einfachen Arrays, die wiederrum durch Funktionen (Getter/Setter) aufgerufen werden. Ist schon ein wenig "anders" das ganze. ^^

    Hatte eigentlich auch demnächst vor, die Library im Forum zu teilen, sofern sie ausgebaut ist. Wird aber nix großes. ^^

    Durch diese Library könnte man das ganze viel einfacher und übersichtlicher gestalten. Werde ich mir auf jeden Fall mal anschauen. :saint:

    Und großen Respekt, dass du noch nach so vielen Jahren hinter dem Projekt stehst. 8o

  • Vielen Dank für das Lob, ich mache immer viel zu viele Sachen gleichzeitig, weshalb einiges oft für Ewigkeiten auf der Strecke bleibt...

    Vermutlich werde ich eines schönen Tages die Vorschläge von BugFix umsetzen, das wäre ansich schon wichtig. Außerdem gibt es noch ein paar Bugs/Aufräumarbeiten...

    Für GDI+ "Objekte" kannst du den Datentyp ptr nutzen (denn nichts anderes sind die "hGFX" und "hBMP" und so weiter, alles nur Pointer auf ein Stückchen Speicher).

    lg

    M

  • Mars 18. März 2019 um 19:40

    Hat den Titel des Themas von „AutoItObjectEx (Objekte in AutoIt)“ zu „NativeAutoItObjectEx (Objekte in AutoIt)“ geändert.