AutoItSimpleObjects - Objektorientierung in AutoIt

  • Hallo zusammen,

    ich möchte Euch hier eine erste Version von AutoItSimpleObjects vorstellen. Das Ziel ist es, erstes Feedback zu sammeln, um zu erfahren, wohin die Reise gehen soll - also was man sich als Community unter objektorientierung in AutoIt vorstellt, welche Funktionalität man sich dabei wünscht, und wie man damit einen Mehrwert für die Sprache bringen kann. Ich finde es schade, dass AutoItObject nicht mehr weiterentwickelt wird, und AutoItObject_Internal nur Attribute, setter()- und getter()- kann. Deswegen möchte ich das Thema aufgreifen. DISCLAIMER: Alles was ich im Folgenden über AutoItSimpleObjects sage, beschreibt den aktuellen Zustand - nicht immer unbedingt das finale Ziel. Wenn ich also z.B. sage, es gibt nur öffentliche Attribute - dann heißt das nicht, dass das so bleiben muss.

    Wer noch nie von objektorientierter Programmierung gehört hat, tut sich gut daran, ggf. eine kurze Runde durch das Internet zu drehen. Starten kann man z.B. hier: https://de.wikipedia.org/wiki/Objektorientierung

    1. Was kann AutoItSimpleObjects?* (*Stand jetzt)

    AutoItSimpleObjects (kurz "ASO") ermöglicht die Erzeugung von einfachen Objekten, die Attribute und -Methoden besitzen können. Im Gegensatz zu AutoItObject werden dafür keine zusätzlichen Dateien wie z.B. DLL's benötigt.

    Die Erzeugung von Objekten, Zuweisung von Methoden oder das Anlegen von Attributen ist dabei stark an das originale AutoItObject angelehnt - Alle Attribute und Methoden sind allerdings öffentlich.

    AutoItSimpleObjects unterstützt 64-Bit und zur Not auch ByRef Parameter in Methodenaufrufen. Objekte können kopiert werden, was eine Art der Pseudo-Vererbung ermöglicht.

    AutoIt
    $oObject = _ASO_Create() ; Create Object
    AutoIt
    _ASO_AddProperty($oObject, 'test') ; Add Property
    AutoIt
    _ASO_AddMethod($oObject, 'Method1', _method1) ; Add Method

    2. Was ist AutoItSimpleObjects?

    Technisch gesehen ist AutoItSimpleObjects ein Wrapper zu AutoItObject_Internal, der mit einer eigenen Invoke-Methode und einigen wenigen Komfortfunktionen daherkommt.

    AutoItObject beinhaltet somit die gesamte Funktionialität von AutoItObject_Internal und fügt ihr folgende wesentliche Merkmale hinzu:

    • Die eigene Invoke-Methode ermöglicht den Aufruf von Methoden, die nicht __getter- oder __setter-Methoden sind.
    • Alle Objekte haben eine zusätzliche Eigentschaft "__dirty".

    Die Objekte, die in ASO erzeugt werden, sind Instanzen von IDispatch - Zugriff auf Attribute und Methodenaufrufe sind also Callbacks - deswegen sollten Methoden klein und kurz gehalten werden. (Bitte keine GUI-Messageloops in Methoden. :))

    3. Was kann AutoItSimpleObjects (noch) NICHT?

    • Vererbung
    • Klassen
    • Sichtbarkeiten
    • Interfaces

    4. Wie verwende ich AutoItSimpleObjects / Was gibt es zu beachten?

    Im nachfolgenden ein kurzes Beispiel zur Anlage eines einfachen Objekts, der Zuweisung von Attributen und der Definition von Methoden:

    AutoItSimpleObjects Example.au3

    Ansonsten kann AutoItSimpleObjects genau wie AutoItObject_Internal genutzt werden. Siehe dazu die Beispiele auf GitHub. Es gilt folgendes zu beachten:

    • Im Gegensatz zu AutoItObject benötigen Funktionen keinen zusätzlichen Parameter $oSelf als ersten Parameter der Funktion. Die Deklaration von Methoden wird dadurch einfacher.
    • Der Zugriff auf das aktuelle Objekt innerhalb einer Methode erfolgt stattdessen mittels Aufruf von $oThis = _ASO_This() am Anfang der Funktion.
    • Der Zugriff auf eine Methode $oObject.Methodname() ohne Klammern (also $oObject.Methodname) ruft die zugewiesene Funktion ohne Parameter auf, statt eine Funktionsreferenz zurückzuliefern. Das ist technisch bedingt, weil uns AutoIt nicht ermöglicht, zwischen Attributszugriff und Methodenaufruf ohne Parameter zu unterscheiden. Zeile 48 könnte also auch so lauten: $vRet = $oObject.Method3**
    • Die Nutzung von ByRef-Parametern in einem Methodenaufruf erfordert es, die Funktionsreferenz in einer separaten Variable zu speichern, siehe Zeile 30 ff. Dabei muss die Funktionsreferenz unbedingt mit $oObject.__get("Methodname") geholt werden - ein Zugriff mittels $pFunc = $oObject.Methodname führt zu einem Fehler. Siehe vorheriger Punkt.**
    • Die Methoden _ASO_AddMethod(...) und _ASO_AddProperty(...) sind eigentlich überflüssig. Attribute müssen nicht explizit angelegt werden, sondern werden bei der ersten Zuweisung automatisch Public erstellt und Methoden werden dadurch erstellt, dass einem Attribute eine Funktionsreferenz zugewiesen wird. Zeile 6-11 im oberen Beispiel können also auch wie folgt lauten:
    AutoIt
    $oObject = _ASO_Create()
    $oObject.test = ""
    $oObject.Method1 = __method1
    $oObject.Method2_with_byref = _TestMethodByref
    $oObject.Method3 = _method3

    ** dieses Verhalten lässt sich durch das setzen des __dirty-Attributs eines Objektes auf 'True' ändern; ist ein Objekt __dirty, so wird es beim Zugriff auf eine Methode die Funktionsreferenz der zugewiesenen Funktion zurückliefern, statt sie ohne Parameter aufzurufen. Das hat zur Folge, dass Methoden ohne Parameter nur mit dem AutoIt Schlüsselwort "Call" aufgerufen werden können, oder die Funktionsreferenz in einer separaten Variable gespeichert werden muss, um sie ohne Parameter aufzurufen. Auf der anderen Seite können mit gesetztem __dirty-Flag ByRef-Parameter verwendet werden, ohne die Funktionsreferenz vorher explizit in einer Variable zu speichern.


    Siehe dazu nachfolgendes Beispiel:

    AutoItSimpleObjects Example_dirty.au3

    Spoiler anzeigen

    Zu beachten ist hier noch einmal folgende Kurzschreibweise, speziell für/mit ByRef-Paremetern.

    In Zeile 1 ist ($oObject.Method2_with_byref) eingeklammert; deswege wird zuerst die Funktionsreferenz von Method2_with_byref aufgelöst. Danach wird die Funktion "normal" von AutoIt aufgerufen.

    In Zeile 2 wird die Methode über das IDispatch-Interface aufgerufen, das keine ByRef Paramter unterstützt. FYI: Ohne gesetztes __dirty-Flag würde $oObject.Method2_with_byref in Zeile 1 über IDispatch ohne Parameter aufgerufen.

    AutoIt
    $vRet = ($oObject.Method2_with_byref)($aArray, "New Value!")
    $vRet = $oObject.Method2_with_byref($aArray, "doesn't work :(")

    5. Was brauche ich von Euch?

    Anwendungsfälle, Ideen, Feedback.

    Mal ins Blaue gedacht: Wofür würdet Ihr OO- benutzen? Sind Dinge wie Interfaces, Klassen, Traits, ByRef-Paramter, usw. für Euch unabdingbar bzw. was wäre Funktionalität, die Ihr nicht braucht?

    Ist die UDF einfach genug - oder zu einfach gehalten? Sind die Syntaxmöglichkeiten in AutoIt ausreichend?

    Die UDF: AutoItSimpleObjects.au3

    siehe Post #2, oder Anhang

    AutoItSimpleObjects Example.au3 / AutoItSimpleObjects Example_dirty.au3

    s.o.

  • AutoItSimpleObjects.au3

    Spoiler anzeigen
  • Hi SEuBo.

    Ich würde mich über objektorientiertes AutoIt freuen! :thumbup:

    5. Was brauche ich von Euch?

    Anwendungsfälle, Ideen, Feedback.

    Mal ins Blaue gedacht: Wofür würdet Ihr OO- benutzen?

    Was ich mir schon oft gewünscht habe, ist das intuitive Benutzen von Funktionen und vorallem von Eigenschaften! AutoIt funktioniert und ist großartig! Dennoch habe ich schon oft gedacht "Meine Güte, wie war das noch mal? Wie bekomme ich den Text von einem Edit, oder die Info, ob eine CheckBox angehakt ist?" Kurz durch die AutoIt-Hilfe gehangelt und irgendwann wusste ich es wieder. Bei Dingen, die ich nicht so oft nutze, hab ich das nach ein paar Wochen wieder vergessen und muss wieder suche.

    Mit VB und Delphi habe ich lange programmiert. Gerade am Anfang war es eine große Hilfe, wenn ich in der IDE z.B. "Edit1" geschrieben habe und einen Punkt dahinter, und dann ein Fensterchen aufgepoppt ist, das ich einfach von oben bis unten durchscrollen konnte, bis ich z.B. "Text" gefunden habe. Oder "Label1.Caption"! Ich finde das sehr intuitiv!

    Noch ein Beispiel.

    MyStringList := TStringList.Create;

    MyStringList.Add("String")

    MyStringList.LoadFromFile("FileName")

    ...

    Man hat einfach ein paar Möglichkeiten, wo man ansetzen kann. :thumbup:

    Ansonsten: Da hast du dir ein großes Projekt vorgenommen! Ich würde mich freuen, wenn das richtig erfolgreich wird! :)

    Wenn jemand sagt: "Das geht nicht!" Denke daran: Das sind seine Grenzen, nicht deine.

    • Offizieller Beitrag

    5. Was brauche ich von Euch?

    Anwendungsfälle, Ideen, Feedback.

    Mal ins Blaue gedacht: Wofür würdet Ihr OO- benutzen? Sind Dinge wie Interfaces, Klassen, Traits, ByRef-Paramter, usw. für Euch unabdingbar bzw. was wäre Funktionalität, die Ihr nicht braucht?

    Ist die UDF einfach genug - oder zu einfach gehalten? Sind die Syntaxmöglichkeiten in AutoIt ausreichend?

    Ich hatte vor Jahren mit AutoIt-Object mal die String- und später Teile der GUI-Funktionen in Objektform dargestellt. (Link)

    Für GUI mit vielen gleichartigen Control ist eine Methode .Clone natürlich fein. Aber das ist natürlich in erster Linie Syntactic Sugar.

    Aber sicherlich ist eine Portierung der AutoIt-Funktionen nicht das Ziel.

    Wofür also ist OOP wünschenswert?

    Das kann ich ad hoc gar nicht mal sagen. Was ich in AutoIt prozedural programmiere mache ich z.B. in Nim mit OO. Die Sprache bietet es an, also nutze ich es - schon allein wegen der angenehmeren Syntax.

    Achja, was ich angenehm finde (denn das stört mich bei AutoIt):

    Die Parameter in Methoden sollten über ihren Namen angesprochen werden können. Also wenn ich z.B. habe

    Method(Param1=Default, Param2=Default, Param3) ,

    rufe ich auf mit

    .Method(Param3=Wert3)

    statt

    .Method(Default, Default, Wert3).

  • Mir ist gerade ein Paradebeispiel über den Weg gelaufen: Die CheckBox.

    Für die CheckBox muss man sich verschiedene Funktionen zusammensuchen, die einem nicht so richtig intuitiv einfallen (zumindes mir nicht). 8o

    GUICtrlSetState() Häkchen setzen/entfernen.
    GUICtrlGetState()
    GUICtrlRead()
    Prüfen, ob das Häkchen gesetzt ist.
    GUICtrlSetData() Text zuweisen.
    GUICtrlGetData()
    GUICtrlRead() mit advanced = 1
    Text auslesen.


    Bei einem Objekt und dessen Eigenschaften würde ich mir das so vorstellen:

    CheckBox1.Checked = True/False Häkchen setzen/entfernen.
    If CheckBox1.Checked Then Prüfen, ob das Häkchen gesetzt ist.
    CheckBox1.Text = "Hallo Welt!" Text zuweisen.
    $sChkText = CheckBox1.Text Text auslesen.

    Vielleicht sieht das mancher anders, aber ich finde das total easy und eingängig. :)

    Wenn jemand sagt: "Das geht nicht!" Denke daran: Das sind seine Grenzen, nicht deine.

    • Offizieller Beitrag

    Mir ist gerade ein Paradebeispiel über den Weg gelaufen: Die CheckBox.

    Für die CheckBox muss man sich verschiedene Funktionen zusammensuchen, die einem nicht so richtig intuitiv einfallen (zumindes mir nicht). 8o

    GUICtrlSetState() Häkchen setzen/entfernen.
    GUICtrlGetState()
    GUICtrlRead()
    Prüfen, ob das Häkchen gesetzt ist.
    GUICtrlSetData() Text zuweisen.
    GUICtrlGetData()
    GUICtrlRead() mit advanced = 1
    Text auslesen.


    Bei einem Objekt und dessen Eigenschaften würde ich mir das so vorstellen:

    CheckBox1.Checked = True/False Häkchen setzen/entfernen.
    If CheckBox1.Checked Then Prüfen, ob das Häkchen gesetzt ist.
    CheckBox1.Text = "Hallo Welt!" Text zuweisen.
    $sChkText = CheckBox1.Text Text auslesen.

    Vielleicht sieht das mancher anders, aber ich finde das total easy und eingängig. :)

    Das wäre ja dann eine Portierung von GUI Funktionen in ein Objekt. Das kannst du natürlich selbst durchführen und ist in deiner beschriebenen Form ja möglich.

    SEuBo geht es ja darum, was das Objekt als Solches sonst so können sollte. Die Implementierung von GUI etc. ist ja absolut unproblematisch und es liegt dann an dir, wie du das in deiner Variante umsetzt

    • Offizieller Beitrag

    SEuBo

    Ich habe mit dem DL aus Post1 getestet.

    Was ich nicht ganz verstehe:

    Im 2.ten Bsp. möchte ich als Parameter die Property eines anderen Objekts übergeben, das geht daneben.

    Da hatte ich mich vertan - funktioniert

    Test
    Code
    ; fehlende Deklarationen
    ; in "AutoItObject_internal.au3" - Func: "Invoke" - "$b", "$pVARIANT_Getter" und "$tVariantSource"
    ;                                - Func: "__AOI_PropertyGetFromName" - "$tProperty", "$sPropertyName"
    ;                                - Func: "__AOI_PropertyCreate" - "$tProperty", "$sPropertyName"
    
    Das sind die, die ich bemerkt hatte. Danach habe ich die Option (MustDeclareVars) deaktiviert.


    EDIT:

    Aber Verkettung scheint so nicht machbar.

    Die Methoden reichen ja beim Verlassen die Objektreferenz weiter. Eigentlich sollte doch somit die Folgemethode aufgerufen werden können.

  • Das wäre ja dann eine Portierung von GUI Funktionen in ein Objekt.

    IMHO sind Object.Checked und Object.Text keine Funktionen sondern Eigenschaften, auf die lesend und schreibend zugegriffen werden kann. Oder meinst du was anderes? :/

    Wenn jemand sagt: "Das geht nicht!" Denke daran: Das sind seine Grenzen, nicht deine.

    • Offizieller Beitrag

    Oder meinst du was anderes?

    Ich meinte deinen Bezug auf Checkbox. Das ist ein GUI-Element und somit grundsätzlich nicht Bestandteil einer Objekt-UDF.

    Aber natürlich kann man mit Basis der Objekt-UDF dann eine GuiObjekt-UDF erstellen.

  • Ah, gut dass du das sagst! :thumbup: Mir ist die ganze Zeit im Sinn rumgegeistert, dass ich Objektorientierung tatsächlich in erster Linie auf GUI Elemente beziehe.

    Mal ins Blaue gedacht: Wofür würdet Ihr OO- benutzen?

    Somit kennst du meine Vorstellgung. 8o

    BugFix Nun habe ich das Gefühl, Objektorientierung falsch verstanden zu haben. Wenn nicht für GUI Elemente, wofür würdest du Objekte vorrangig haben wollen? :)

    Wenn jemand sagt: "Das geht nicht!" Denke daran: Das sind seine Grenzen, nicht deine.

    • Offizieller Beitrag

    Wenn nicht für GUI Elemente, wofür würdest du Objekte vorrangig haben wollen? :)

    Ich finde es schon interessant, das für GUI zu nutzen. Letztlich kann man es ja für alles nutzen. Aber wir brauchen ja erst mal die Basis, die das kann. Du tapezierst ja auch nicht, bevor du die Wände gemauert hast. 8o


    EDIT:

    Hier mal ein ganz minimalistisches Bsp. um mit ASO und GUI zu arbeiten:

    • Offizieller Beitrag

    SEuBo

    Eine Bitte:

    Deklariere alle Variablen. Ich vermute, dass das Abstürzen damit zusammenhängt.

    Lass einfach mal mein Prüfskript drüberlaufen um alle nicht deklarierten Variablen zu finden.

    EDIT

    Da habe ich jetzt aber das falsche skript eingestellt (man wird alt). Dieses zeigt dir ja nur (un)genutzte Variablen.

    Das Skript, welches ich meinte, war "SetVarsDeclared". Da es aber von 2014 ist, werde ich das erst nochmal anschauen. Zumal es auch ein AutoIt-Skript ist. Für diesen Zweck ist Lua geeigneter.


    Kannst du in SciTE so einbinden (Pfade anpassen):

    Code
    # 39 Unused Vars 
    command.39.*.au3="$(autoit3dir)\autoit3.exe" "C:\CODE\AutoIt\TEST\GetUnusedVars.au3" "$(FilePath)" "0" "1"
    command.name.39.*.au3=GetUnusedVars
    command.save.before.39.*.au3=1
    command.is.filter.39.*.au3=1
    command.shortcut.39.*.au3=Ctrl+Shift+Alt+U
    command.mode.39.*=subsystem:1

    Dazu brauchst du die:

  • BugFix Darf ich dein Script "GetUnusedVars.au3" in PSPad4AutoIt3 benutzen und verteilen? Ich würde mich freuen! :)

    Übrigens, wenn ich mich nicht vertan habe, müsste dein Script bei sich selbst auch eine ungenutze Variable zeigen: $error. 8o

    Wenn jemand sagt: "Das geht nicht!" Denke daran: Das sind seine Grenzen, nicht deine.