diff --git a/AHKL_ChangeLog.htm b/AHKL_ChangeLog.htm index bca6ce36..99abaf1f 100644 --- a/AHKL_ChangeLog.htm +++ b/AHKL_ChangeLog.htm @@ -15,6 +15,14 @@
Ältere Änderungen können in Archivierte Änderungen gefunden werden.
+Geändert: Hotkey, If, Ausdruck
löst nun einen Fehler aus, wenn der nicht-dokumentierte dritte Parameter benutzt wird.
Behoben: &&
, ||
und der ternäre Operator geben nun jedes Objekt frei, das als Bedingung benutzt wurde, wie bei if (a.Unterobjekt && b)
.
Behoben: Gui/GuiControl konnte Radio-Buttons innerhalb eines Tab3-Steuerelements nicht markieren.
+Behoben: FileCreateShortcut's Verknüpfung-Parameter kann in Windows 10 absofort einen relativen Pfad enthalten.
+Behoben: Ein Tab3-Steuerelement mit aktivierter Theme-Option wird die benutzerdefinierte Farbe seiner Steuerelemente nicht länger überschreiben.
+Behoben: Der Debugger behandelte Ablaufsteuerungsbefehle in einigen bestimmten Fällen nicht korrekt.
+Der Versuch, eine leere Variable oder einen Ausdruck, der zu einem leeren Wert führt, an SetTimer's Label-Parameter zu übergeben, wird absofort als Fehler eingestuft. Dieser Parameter darf weder leer sein noch fehlen.
@@ -541,7 +549,7 @@Behoben: Dynamische Funktionsaufrufe mit Built-in-Variablen wie z. B. %A_ThisLabel%()
.
Verbessert: %var%()
unterstützt nun Funktionsverweise, Funktion-nachahmende Objekte und die __Call-Meta-Standardfunktion.
Verbessert: %var%()
unterstützt nun Funktionsobjekte und die __Call-Meta-Standardfunktion.
Behoben: ControlGet List funktioniert nun bei ListViews, wenn das Skript und der Zielprozess nicht im 32-Bit-Format sind, oder alle beide im 64-Bit-Format sind.
Behoben: SendEvent mit einer Tastenverzögerung von 0; eine Änderung, die in v1.1.05.04 eigenführt wurde, bewirkte, dass SendEvent langsamer war, als gedacht.
Behoben: Object.Remove(i) passte die Keys nicht an, wenn Object[i] nicht existierte.
diff --git a/AutoHotkey.htm b/AutoHotkey.htm index 7efab693..1ac4bfcf 100644 --- a/AutoHotkey.htm +++ b/AutoHotkey.htm @@ -11,7 +11,7 @@ -Eine deutsche Übersetzung von https://autohotkey.com/docs/ (siehe hier für mehr Details).
©2003-2014 Chris Mallett, und zum Teil ©AutoIt-Team und die AHK-Community
Software-Lizenz: GNU General Public License
diff --git a/ChangeLogHelp.htm b/ChangeLogHelp.htm index 76a6d5e9..bed7d761 100644 --- a/ChangeLogHelp.htm +++ b/ChangeLogHelp.htm @@ -86,7 +86,7 @@Hinzugefügt: GUI-Steuerelement "Tab2" behebt im originalen "Tab"-Steuerelement seltene Probleme beim Neuzeichnen (z. B. beim Aktivieren eines GUI-Fensters durch Anklicken der Scrollleiste eines Steuerelements). Das originale Tab-Steuerelement wird aufgrund der Abwärtskompatibilität beibehalten, weil "Tab2" das Tab-Steuerelement erst einfügen wird, wenn es Steuerelemente in Navigationsreihenfolge der TAB-Taste enthält. [Danke an Xander]
Behoben: Up-Tasten-Hotkeys wie a up::
blockieren nicht länger das Drücken der A-Taste, sofern die #IfWin-Kriterien des Hotkeys nicht erfüllt sind. [Danke an Roland]
Behoben: Round(Var, NegativeZahl)
ging in einigen Fällen um 1 zurück. [Danke an Icarus]
Behoben: Round(Var, NegativeZahl)
ging in einigen Fällen um 1 zurück. [Danke an Icarus]
Behoben: Es wird nicht länger ein Absturz verursacht, wenn ein Syntaxfehler erfolgt, der aus einer verwaisten IF-Anweisung besteht (fehlerhaft seit 1.0.47.00). [Danke an msgbox vom deutschen Forum]
Entfernt: Fehlermeldung "GetClipboardData". Stattdessen wird eine leere Zeichenkette abgerufen, wenn die Daten innerhalb der #ClipboardTimeout-Periode nicht zugreifbar sind. [Danke an ManaUser & Sean]
Geändert: GUI-CheckBoxen und -Radio-Buttons verwenden nun standardmäßig "keinen Zeilenumbruch", wenn keine Breite, Höhe oder CR/LF-Zeichen angegeben sind. Dadurch werden Anzeigeprobleme bei ungewöhnlichen DPI-Einstellungen gelöst. [Danke an Boskoop]
diff --git a/Functions.htm b/Functions.htm index 4cdafcc8..5fd419ba 100644 --- a/Functions.htm +++ b/Functions.htm @@ -219,7 +219,7 @@Oft auftretende Verwechslungsgefahr: Jeder nicht-dynamische Verweis auf eine Variable erstellt diese Variable in dem Moment, wo das Skript gestartet wird. Zum Beispiel: Außerhalb einer Funktion würde MsgBox %Array1%
die Array1-Variable als globale Variable erstellen, in dem Moment, wo das Skript gestartet wird. Innerhalb einer Funktion würde MsgBox %Array1%
die Array1-Variable als lokale Variable erstellen, in dem Moment, wo das Skript gestartet wird (solange der global-behandelnder Modus nicht aktiv ist), selbst wenn Array und Array0 als global deklariert sind.
Seit v1.0.47.06 kann eine Funktion (auch eine Built-in-Funktion) mithilfe von Prozentzeichen dynamisch aufgerufen werden. Zum Beispiel würde %Var%(x, "Fuchs")
die Funktion aufrufen, deren Name in Var enthalten ist. Ebenso würde Funktion%A_Index%()
Funktion1(), Funktion2() und so weiter aufrufen, abhängig vom aktuellen Wert in A_Index.
Seit v1.1.07.00 kann Var in %Var%()
einen Funktionsnamen, Funktionsverweis oder ein Funktion-nachahmendes Objekt enthalten. Falls die Funktion nicht existiert, wird stattdessen die __Call-Meta-Funktion des Standard-base-Objekts aufgerufen.
Seit v1.1.07.00 kann Var in %Var%()
einen Funktionsnamen oder ein Funktionsobjekt enthalten. Falls die Funktion nicht existiert, wird stattdessen die __Call-Meta-Funktion des Standard-base-Objekts aufgerufen.
Kann die Funktion aufgrund einer der unten genannten Gründe nicht aufgerufen werden, stoppt die Auswertung des Ausdrucks, der den Aufruf enthält, vorzeitig ohne Meldung, was zu widersprüchlichen Ergebnissen führen könnte:
If IsFunc(VariableMitFunktionsname)
verhindert werden kann. Die Definition der aufgerufenen Funktion (außer Built-in-Funktionen) muss explizit im Skript vorkommen (zum Beispiel über #Include oder einem nicht-dynamischen Aufruf einer Library-Funktion).RegisterCallback(): Erstellt eine Maschinencode-Adresse, die, wenn sie aufgerufen wird, den Aufruf an eine Funktion im Skript weiterleitet. Siehe RegisterCallback() für Details.
Trim() [AHK_L 31+]: Entfernt bestimmte Zeichen vom Anfang und/oder Ende einer Zeichenkette. Siehe Trim() für Details.
VarSetCapacity(VarName [, BenötigteKapazität, Füllbyte]): Vergrößert die Aufnahmekapazität einer Variable oder gibt deren Speicher frei. Siehe VarSetCapacity() für Details.
-Hinweis: Mathematische Funktionen geben generell einen leeren Wert (leere Zeichenkette) zurück, falls einer der eingehenden Parameter nicht numerisch ist.
-Abs(Zahl): Gibt den absoluten Wert von Zahl zurück. Der Rückgabewert hat den gleichen Typ wie Zahl (Integer oder Gleitkommazahl).
-Ceil(Zahl): Gibt Zahl zurück, aufgerundet auf den nächsten Integer (ohne .00-Suffix). Zum Beispiel ist Ceil(1.2)
gleich 2 und Ceil(-1.2)
gleich -1.
Exp(N): Gibt e zurück (ungefähr 2,71828182845905), potenziert mit N. Der Parameter N kann negativ sein und einen Dezimalpunkt enthalten. Um neben e noch andere Zahlen zu potenzieren, verwende den **-Operator.
-Floor(Zahl): Gibt Zahl zurück, abgerundet auf den nächsten Integer (ohne .00-Suffix). Zum Beispiel ist Floor(1.2)
gleich 1 und Floor(-1.2)
gleich -2.
Log(Zahl): Gibt den Logarithmus (Basis 10) von Zahl zurück. Das Ergebnis ist eine Gleitkommazahl. Wenn Zahl negativ ist, wird eine leere Zeichenkette zurückgegeben.
-Ln(Zahl): Ermittelt den Logarithmus (Basis e) von Zahl. Das Ergebnis ist eine Gleitkommazahl. Wenn Zahl negativ ist, wird eine leere Zeichenkette zurückgegeben.
-Mod(Dividend, Divisor): Modulo. Gibt den Rest zurück, wenn der Dividend durch den Divisor geteilt wird. Das Vorzeichen des Ergebnisses entspricht dem Vorzeichen des ersten Parameters. Zum Beispiel sind Mod(5, 3)
und Mod(5, -3)
gleich 2, aber Mod(-5, 3)
und Mod(-5, -3)
gleich -2. Wenn einer der beiden Parameter eine Gleitkommazahl ist, wird das Ergebnis ebenfalls eine Gleitkommazahl sein. Zum Beispiel ist Mod(5.0, 3)
gleich 2.0 und Mod(5, 3.5)
gleich 1.5. Ist der zweite Parameter eine 0, gibt die Funktion ein leeres Ergebnis (leere Zeichenkette) zurück.
Round(Zahl [, N]): Ist N nicht vorhanden oder eine 0, wird Zahl auf den nächsten Integer gerundet. Ist N eine positive Zahl, wird Zahl auf N Dezimalstellen gerundet. Ist N negativ, wird Zahl auf N Stellen nach links gerundet. Zum Beispiel ist Round(345, -1)
gleich 350 und Round(345, -2)
gleich 300. Im Gegensatz zu Transform Round hat das Ergebnis keinen .000-Suffix, wenn N weggelassen wird oder kleiner als 1 ist. Seit v1.0.44.01 zeigt ein Wert in N, der größer als 0 ist, genau N Dezimalstellen an, anstatt SetFormat zu berücksichtigen. Man kann das verhindern, wenn man eine weitere mathematische Operation beim Rückgabewert von Round() durchführt; zum Beispiel: Round(3.333, 1)+0
.
Sqrt(Zahl): Gibt die Quadratwurzel von Zahl zurück. Das Ergebnis ist eine Gleitkommazahl. Wenn Zahl negativ ist, gibt die Funktion ein leeres Ergebnis (String) zurück.
-Sin(Zahl) | Cos(Zahl) | Tan(Zahl): Gibt den trigonometrischen Sinus|Kosinus|Tangens von Zahl zurück. Zahl muss als Bogenmaß angegeben werden.
-ASin(Zahl): Gibt den Arkussinus (die Zahl, deren Sinus Zahl ist) als Bogenmaß zurück. Wenn Zahl kleiner als -1 oder größer als 1 ist, gibt die Funktion ein leeres Ergebnis (leere Zeichenkette) zurück.
-ACos(Zahl): Gibt den Arkuskosinus (die Zahl, deren Kosinus Zahl ist) als Bogenmaß zurück. Wenn Zahl kleiner als -1 oder größer als 1 ist, gibt die Funktion ein leeres Ergebnis (leere Zeichenkette) zurück.
-ATan(Zahl): Gibt den Arkustangens (die Zahl, deren Tangens Zahl ist) als Bogenmaß zurück.
-Hinweis: Soll Bogenmaß in Grad umgewandelt werden, multipliziere es mit 180/pi (ungefähr 57.29578). Um ein Grad-Wert in Bogenmaß umzuwandeln, kann es mit pi/180 (ungefähr 0.01745329252) multipliziert werden. Der Wert von pi (ungefähr 3.141592653589793) ist viermal der Arkustangens von 1.
+Klicke auf einen Funktionsnamen, um mehr darüber zu erfahren.
+Funktion | +Beschreibung | +
---|---|
Abs | +Gibt den absoluten Wert von Zahl zurück. | +
Ceil | +Gibt Zahl zurück, welche auf den nächsten Integer aufgerundet ist (ohne .00-Suffix). | +
Exp | +Gibt e zurück (ungefähr 2.71828182845905), potenziert mit N. | +
Floor | +Gibt Zahl zurück, welche auf den nächsten Integer abgerundet ist (ohne .00-Suffix). | +
Log | +Gibt den Logarithmus (Basis 10) von Zahl zurück. | +
Ln | +Ermittelt den Logarithmus (Basis e) von Zahl. | +
Mod | +Gibt den Rest zurück, wenn Dividend durch Divisor geteilt wird. | +
Round | +Gibt Zahl zurück, welche auf N Dezimalstellen gerundet ist. | +
Sqrt | +Gibt die Quadratwurzel von Zahl zurück. | +
Sin / Cos / Tan | +Gibt den trigonometrischen Sinus/Kosinus/Tangens von Zahl zurück. | +
ASin / ACos / ATan | +Gibt den Arkussinus/Arkuskosinus/Arkustangens als Bogenmaß zurück. | +
Befehlsfunktionen von Polyethene: Bietet für jeden AutoHotkey-Befehl, der eine Ausgabevariable hat, eine aufrufbare Funktion. Diese Library kann per #Include in jedem Skript eingefügt werden.
diff --git a/Hotkeys.htm b/Hotkeys.htm index 2ea15c26..7b08084a 100644 --- a/Hotkeys.htm +++ b/Hotkeys.htm @@ -20,7 +20,8 @@Mithilfe von " & " kann eine benutzerdefinierte Kombination von zwei Tasten (außer Joystick-Tasten) definiert werden. Im folgenden Beispiel würde der Hotkey ausgelöst werden, wenn man Numpad0 gedrückt hält und danach die zweite Taste drückt:
+Mithilfe von " & " kann eine benutzerdefinierte Kombination von zwei Tasten (außer Joystick-Tasten) definiert werden. Im folgenden Beispiel würde der Hotkey ausgelöst werden, wenn man Numpad0 gedrückt hält und danach die zweite Taste drückt:
Numpad0 & Numpad1::MsgBox, Sie haben Numpad1 gedrückt, während Numpad0 gedrückt wurde. Numpad0 & Numpad2::Run Notepad-
Im obigen Beispiel wird Numpad0 eine Präfix-Taste; allerdings verliert Numpad0 dadurch seine ursprüngliche Funktion, wenn sie selbst gedrückt wird. Um das zu vermeiden, kann man die Numpad0-Taste wie folgt konfigurieren, damit sie eine neue Aktion durchführen kann:
+Die Präfixtasten verlieren ihre ursprüngliche Funktion: Im obigen Beispiel wurde Numpad0 als Präfixtaste definiert; wenn man nur diese Taste drückt, wird nichts passieren. Um das zu vermeiden, kann man die Numpad0-Taste wie folgt konfigurieren, damit sie eine neue Aktion durchführen kann:
Numpad0::WinMaximize A ; Maximiert das aktive/vorderste Fenster. Numpad0::Send {Numpad0} ; Numpad0 erzeugt beim Loslassen eine Numpad0-Tastatureingabe. Siehe unteren Kommentar.-
Das Vorhandensein einer der oben genannten benutzerdefinierten Tastenkombinationen bewirkt, dass Numpad0 losgelassen wird, um die angegebene Aktion durchführen zu können, aber nur, wenn man keine andere Taste drückte, während Numpad0 gedrückt gehalten wurde. Seit v1.1.14 kann dieses Verhalten durch Anfügen des Tilde-Präfixes an beiden Hotkeys unterdrückt werden.
+Auslösen beim Loslassen: Das Vorhandensein einer der oben genannten benutzerdefinierten Tastenkombinationen bewirkt, dass Numpad0 losgelassen wird, um die angegebene Aktion durchführen zu können, aber nur, wenn man keine andere Taste drückte, während Numpad0 gedrückt gehalten wurde. Seit v1.1.14 kann dieses Verhalten durch Anfügen des Tilde-Präfixes an beiden Hotkeys unterdrückt werden.
+Modifikatoren: Im Gegensatz zu normalen Hotkeys verhalten sich benutzerdefinierte Hotkeys so, als gelte bei ihnen standardmäßig der Platzhalter-Modifikator (*). Zum Beispiel wird 1 & 2::
auch dann aktiviert, wenn man STRG oder ALT gedrückt hält, während ^1::
nur via STRG+1 aktiviert werden kann, und nicht via STRG+ALT+1.
In Verbindung mit vordefinierten Modifikatortasten funktionieren normale Hotkeys in der Regel besser als "benutzerdefinierte" Kombinationen. Zum Beispiel wäre <+s::
empfehlenswerter als LShift & s::
.
Kombinationen von drei oder mehr Tasten werden nicht unterstützt. Kombinationen, die deine Tastatur-Hardware unterstützt, können in der Regel mithilfe von #If und GetKeyState erkannt werden, aber die Ergebnisse können inkonsistent sein. Zum Beispiel:
+; Halte die MENÜ- und ALT-Taste gedrückt, und drücke dann Bindestrich (-). +#if GetKeyState("AppsKey", "P") +Alt & -::MsgBox Hotkey aktiviert. + +; Notwendig, wenn ALT zuerst gedrückt gehalten wird: +#if GetKeyState("Alt", "P") +AppsKey & -::MsgBox Hotkey aktiviert. + +; , & . & -:: +#if GetKeyState(",") && GetKeyState(".") +-::MsgBox+ +
NUM-, FESTSTELL- und ROLLEN-Taste: Diese Tasten können dazu gezwungen werden, "AlwaysOn" (immer an) oder "AlwaysOff" (immer aus) zu sein. Zum Beispiel: SetNumlockState AlwaysOn
.
Explorer-Tastenkürzel überschreiben: Die vordefinierten Hotkeys von Windows wie WIN+E (#e) und WIN+R (#r) können einzeln überschrieben werden, indem man ihnen im Skript einfach eine Aktion zuweist. Siehe "Überschreiben oder Deaktivieren von Hotkeys" für Details.
Alt-Tab ersetzen: Hotkeys können eine alternative Methode für Alt-Tab bereitstellen. Zum Beispiel ermöglichen dir die folgenden zwei Hotkeys, Alt-Tab mit deiner rechten Hand durchzuführen:
diff --git a/KeyList.htm b/KeyList.htm index 77e51d8a..49bee2b8 100644 --- a/KeyList.htm +++ b/KeyList.htm @@ -162,6 +162,9 @@NumpadDiv | -Division | -
NumpadMult | -Multiplikation | -
NumpadAdd | -Addition | -
NumpadSub | -Subtraktion | -
NumpadEnter | -ENTER-Taste | -
In diesem Zusammenhang wird ein Verweis, der in einem Feld eines anderen Objekts gespeichert ist, freigegeben, wenn dieses Feld irgendeinen anderen Wert zugewiesen bekommt oder vom Objekt entfernt wird. Dies gilt auch für Arrays, weil sie eigentlich Objekte sind.
arr := [{}] ; Erstellt ein Array, das ein Objekt enthält. arr[1] := {} ; Erstellt ein zweites Objekt, wodurch das erste Objekt indirekt freigegeben wird. -arr.Remove(1) ; Entfernt und gibt das zweite Objekt frei.+arr.RemoveAt(1) ; Entfernt und gibt das zweite Objekt frei.
Da alle Verweise auf ein Objekt freigegeben werden müssen, bevor das Objekt freigegeben werden kann, können Objekte mit Zirkelbezügen nicht automatisch freigegeben werden. Wenn beispielsweise x.child
auf y
verweist und y.parent
auf x
verweist, würde es nicht genügen, x
und y
zu leeren, weil das Parent-Objekt noch einen Verweis auf das Child-Objekt enthält, und umgekehrt. Um diese Situation in den Griff zu bekommen, muss der Zirkelbezug entfernt werden.
x := {}, y := {} ; Erstellt zwei Objekte. @@ -150,7 +150,8 @@Keys
x[0x10]
, x[16]
und x[00016]
dasselbe. Dies gilt auch für numerische Zeichenketten ohne Dezimalpunkt.x[1]
und x["1"]
nicht dasselbe. Wenn darüber hinaus eine in Anführungszeichen gesetzte Zeichenkette mit einem anderen Wert verkettet wird (wie in "0x" x
), wird das Ergebnis als rein nicht-numerisch angesehen. Allerdings gilt das nicht für Variablen - beispielsweise wären x[1]
und x[y:="1"]
dasselbe. Dieses Problem wird in v2 behoben. Deshalb sollte man verhindern, numerische Literale in Anführungszeichen als Keys zu verwenden.0+1.0
oder Sqrt(y)
) das aktuelle Float-Format erzwingen. Um Widersprüche zu vermeiden und die Verständlichkeit zu verbessern, sollte man verhindern, literale Gleitkommazahlen als Keys zu verwenden.ObjRawSet(Object, "base", "")
oder Object.SetCapacity("base", 0)
, wird sich der base-Key wie ein beliebige Zeichenkette verhalten.Innerhalb einer Methode kann das Pseudo-Schlüsselwort base
verwendet werden, um auf Super-Klassen-Versionen von Methoden zugreifen zu können, oder auf Eigenschaften, die in einer abgeleiteten Klasse überschrieben werden. Zum Beispiel würde base.Methode()
, wenn man es in der Klasse oben definiert, eine Version von Methode aufrufen, die über BaseKlassenname definiert wurde. Meta-Funktionen werden nicht aufgerufen; ansonsten verhält sich base.Methode()
wie BaseKlassenname.Methode.(this)
. Das heißt,
Innerhalb einer Methode kann das Pseudo-Schlüsselwort base
verwendet werden, um auf Super-Klassen-Versionen von Methoden zugreifen zu können, oder auf Eigenschaften, die in einer abgeleiteten Klasse überschrieben werden. Zum Beispiel würde base.Methode()
, wenn man es in der Klasse oben definiert, eine Version von Methode aufrufen, die über BaseKlassenname definiert wurde. Meta-Funktionen werden nicht aufgerufen; ansonsten verhält sich base.Methode()
wie BaseKlassenname.Methode.Call(this)
. Das heißt,
base.Methode()
immer dort das Base der Klasse aufruft, wo die aktuelle Methode definiert wurde, selbst wenn this
von einer Unterklasse von dieser oder einer anderen Klasse vollständig abgeleitet ist.base.Methode()
implizit this
als ersten (versteckten) Parameter übergibt.this
).this
). Der Wert sollte ein Funktionsname oder Funktionsobjekt sein.
Wenn eine Meta-Funktion einen passenden Key im Objekt beinhaltet, aber keinen return
verwendet, ist das Verhalten das gleiche wie, als wäre der Key bereits zu Beginn im Objekt da gewesen. Um zu erfahren, wie __Set funktioniert, siehe Mehrdimensionale Arrays bei Unterklassen.
Wenn die Operation immer noch nicht behandelt wurde, überprüfe, ob es eine Built-in-Methode oder -Eigenschaft ist:
@@ -405,9 +406,11 @@return
ohne Wert ist dasselbe wie return ""
. Dieser Sachverhalt wird vielleicht in einer zukünftigen Version geändert, so dass man mit return
aus einer Meta-Funktion "flüchten" kann, ohne dabei das Standardverhalten überschreiben zu müssen.Mit __Get und __Set können Eigenschaften implementiert werden, deren Werte auf irgendeiner Weise berechnet oder beschränkt sind. Zum Beispiel können sie verwendet werden, um ein "Farbe"-Objekt mit R, G, B und RGB-Eigenschaften zu implementieren, wo tatsächlich nur der RGB-Wert gespeichert wird:
+Mit der Eigenschaftssyntax können Eigenschaften definiert werden, die jedes Mal, wenn sie ausgewertet werden, einen Wert berechnen, aber jede Eigenschaft muss im Voraus bekannt und einzeln im Skript definiert sein. Mit __Get und __Set können hingegen Eigenschaften implementiert werden, die das Skript nicht vorher kennen muss.
+Zum Beispiel könnte man ein "Proxy"-Objekt erstellen, das Eigenschaften über das Netzwerk (oder über einen beliebig anderen Kanal) anfordern könnte. Ein Remote-Server würde dann mit dem Wert von der Eigenschaft antworten, den das Proxy-Objekt dann an seinen Aufrufer weiterleiten könnte. Auch wenn der Name jeder Eigenschaft im Voraus bekannt war, wäre es unlogisch, jede Eigenschaft einzeln in der Proxy-Klasse zu definieren, da jede Eigenschaft dasselbe tun würde (einen Netzwerk-Request senden). Meta-Funktionen erhalten die Eigenschaftsnamen als Parameter und wären daher eine gute Lösung für dieses Problem.
+Eine weitere Verwendungsmöglichkeit von __Get und __Set wäre es, eine Reihe von verwandten Eigenschaften zu implementieren, die Code unter sich aufteilen. In dem Beispiel unten werden sie verwendet, um ein "Farbe"-Objekt mit R, G, B und RGB-Eigenschaften zu implementieren, wo tatsächlich nur der RGB-Wert gespeichert wird:
rot := new Farbe(0xff0000), rot.R -= 5 -cyan := new Farbe(0x00ffff) +cyan := new Farbe(0), cyan.G := 255, cyan.B := 255 MsgBox % "rot: " rot.R "," rot.G "," rot.B " = " rot.RGB MsgBox % "cyan: " cyan.R "," cyan.G "," cyan.B " = " cyan.RGB @@ -419,74 +422,59 @@+Dynamische Eigenschaften
this.RGB := aRGB } + static Verschiebung := {R:16, G:8, B:0} + __Get(aName) { - if (aName = "R") - return (this.RGB >> 16) & 255 - if (aName = "G") - return (this.RGB >> 8) & 255 - if (aName = "B") - return this.RGB & 255 + ; HINWEIS: this.Verschiebung würde hier eine Endlosschleife erzeugen! + Verschiebung := Farbe.Verschiebung[aName] ; Ermittelt die Anzahl der zu verschiebenden Bits. + if (Verschiebung != "") ; Ist sie eine bekannte Eigenschaft? + return (this.RGB >> Verschiebung) & 0xff + ; HINWEIS: Ein 'return' hier würde this.RGB kaputtmachen. } __Set(aName, aWert) { - if aName in R,G,B + if ((Verschiebung := Farbe.Verschiebung[aName]) != "") { - aWert &= 255 + aWert &= 255 ; Kürzt es auf eine geeignete Länge. - if (aName = "R") - this.RGB := (aWert << 16) | (this.RGB & ~0xff0000) - else if (aName = "G") - this.RGB := (aWert << 8) | (this.RGB & ~0x00ff00) - else ; (aName = "B") - this.RGB := aWert | (this.RGB & ~0x0000ff) + ; Errechnet und speichert den neuen RGB-Wert. + this.RGB := (aWert << Verschiebung) | (this.RGB & ~(0xff << Verschiebung)) - ; 'Return' muss verwendet werden, um zu kennzeichnen, dass kein neues Key-Value-Paar erstellt werden sollte. - ; Dies definiert auch, was in 'x' von 'x := clr[name] := val' gespeichert werden soll: + ; 'Return' ist notwendig, um die Erstellung eines neuen Key-Value-Paares zu verhindern. + ; Dies bestimmt auch, was in dem 'x' in 'x := clr[name] := val' gespeichert wird: return aWert } + ; HINWEIS: Ein 'return' hier würde this._RGB und this.RGB kaputtmachen. + } + + ; Meta-Funktionen können mit Eigenschaften vermischt werden: + RGB { + get { + ; Als Hex-Wert zurückgeben: + return format("0x{:06x}", this._RGB) + } + set { + return this._RGB := value + } } }
In diesem Fall hätte man stattdessen die Eigenschaftssyntax verwenden können, wo Code untereinander aufgeteilt werden kann, indem man jede Eigenschaft eine zentrale Methode aufrufen lässt. Meta-Funktionen sollte man wenn möglich vermeiden, da ein hohes Risiko besteht, dass sie falsch verwendet werden (siehe die roten Hinweise oben).
Wenn ein Aufruf wie obj.func(param)
erfolgt, könnte obj.func einen Funktionsnamen oder ein Objekt enthalten. Enthält obj.func ein Objekt, wird dieses Objekt mit obj aufgerufen, anstatt mit dem Methodennamen wie in (obj.func)[obj]()
. Da in den meisten Fällen obj.func[obj]
nicht vorhanden ist, wird stattdessen die __Call-Meta-Funktion von obj.func aufgerufen. Damit könnte man das Verhalten der Funktionsaufrufe auf einer abstrakten Weise ändern, wie es im folgenden Beispiel gezeigt wird:
; Erstellt ein Prototyp für ein Array mit Funktionen. -FuncArrayType := {__Call: "FuncType_Call"} -; Erstellt ein Array mit Funktionen. -funcArray := {1: "Eins", 2: "Zwei", base: FuncArrayType} -; Erstellt ein Objekt, das das Array als Methode verwendet. -obj := {func: funcArray} -; Ruft die Methode auf. -obj.func("foo", "bar") - -FuncType_Call(func, obj, params*) -{ - ; Ruft eine Liste mit Funktionen auf. - Loop % ObjMaxIndex(func) - func[A_Index](params*) -} - -Eins(param1, param2) { - ListVars - Pause -} -Zwei(param1, param2) { - ListVars - Pause -}-
Mit dieser Technik kann ein Objekt erstellt werden, das als Meta-Funktion fungiert, um z. B. dynamische Eigenschaften zu definieren, ähnlich wie die im vorherigen Abschnitt. (Beachte allerdings, dass dieses Beispiel durch die Unterstützung von Eigenschaften ersetzt wurde.) Zum Beispiel:
+Siehe Funktionsobjekte, wie Objekte erstellt werden können, die sich wie Funktionen verhalten.
+Ein Funktionsobjekt kann sich auch wie eine Meta-Funktion verhalten, um z. B. dynamische Eigenschaften zu definieren, die denen im vorherigen Abschnitt ähneln. Obwohl man die Eigenschaftssyntax immer wenn möglich verwenden sollte, zeigt das Beispiel unten das Potenzial von Meta-Funktionen, um neue Konzepte oder Verhaltensmuster zu implementieren, oder um die Struktur des Skripts zu ändern.
blau := new Farbe(0x0000ff) MsgBox % blau.R "," blau.G "," blau.B -class Eigenschaften +class Eigenschaften extends Funktionsobjekt { __Call(aTarget, aName, aParams*) { - ; Falls dieses Eigenschaftsobjekt eine Definition für diese Halb-Eigenschaft enthält, ruft es auf. - ; Achtet darauf, nicht this.HasKey(aName) zu verwenden, da das sonst in __Call rekursiv ausgeführt wird. - if IsObject(aZiel) && ObjHasKey(this, aName) - return this[aName].(aZiel, aParams*) + ; Falls dieses Eigenschaftsobjekt eine Definition für diese Halb-Eigenschaft enthält, wird es aufgerufen. + if ObjHasKey(this, aName) + return this[aName].Call(aZiel, aParams*) } } @@ -600,15 +588,8 @@Debuggen
Implementierung
Referenzzählung
-AutoHotkey verwendet ein einfaches Referenzzählungsmechanismus, um Ressourcen eines Objekts freizugeben, das nicht länger im Skript gebraucht wird. Skript-Autoren sollten diesen Mechanismus nur aufrufen, wenn man direkt mit unverwalteten Pointer, die auf Objekte verweisen, arbeiten muss. Für weitere Informationen, siehe ObjAddRef.
-; Erhöht die Referenzzählung des Objekts, um "es am Leben zu erhalten": -ObjAddRef(Adresse) -... -; Verringert die Referenzzählung des Objekts, so dass es freigegeben werden kann: -ObjRelease(Adresse) --ObjAddRef muss nicht verwendet werden, wenn eine Adresse zunächst per
-Object(obj)
bezogen wird.Generell sollte jede neue Kopie einer Objektadresse als Objektverweis behandelt werden, außer dass das Skript wie vorgesehen für den Aufruf von ObjAddRef und/oder ObjRelease verantwortlich ist. Würde man beispielsweise eine Adresse mit so etwas wie
+x := Adresse
kopieren, sollte ObjAddRef aufgerufen werden, um die Referenzzählung zu erhöhen. Ebenso sollte ObjRelease aufgerufen werden, wenn eine bestimmte Kopie der Objektadresse durchgeführt wurde. Dadurch wird sichergestellt, dass das Objekt freigegeben wird, wenn der letzte Verweis auf dieses Objekt verloren geht - und nicht vorher.AutoHotkey verwendet ein einfaches Referenzzählungsmechanismus, um Ressourcen eines Objekts freizugeben, das nicht länger im Skript gebraucht wird. Skript-Autoren sollten diesen Mechanismus nur aufrufen, wenn man direkt mit unverwalteten Pointer, die auf Objekte verweisen, arbeiten muss.
+In AutoHotkey v1.1 werden temporäre Verweise, die innerhalb eines Ausdrucks erstellt (aber nirgendwo gespeichert) werden, direkt nach ihrer Verwendung freigegeben. Zum Beispiel würde
Fn(&{})
eine ungültige Adresse an die Funktion übergeben, weil der temporäre Verweis, zurückgegeben von{}
, direkt nach dem Auswerten des Adresse-von-Operators freigegeben wird.Wenn Code beim Freigeben des letzten Verweises auf ein Objekt ausgeführt werden soll, muss man die __Delete-Meta-Funktion implementieren.
Bekannte Einschränkungen:
Das Betriebssystem kann den benutzten Speicher vom Objekt wieder verwenden, sobald das Programm beendet wird. Allerdings wird __Delete erst aufgerufen, wenn alle Verweise auf das Objekt freigegeben worden sind. Das kann wichtig sein, wenn andere Ressourcen freigegeben werden, die nicht automatisch vom Betriebssystem wieder verwendet werden, wie zum Beispiel temporäre Dateien.
In einigen seltenen Fällen kann es erforderlich sein, ein Objekt per DllCall an einen externen Code zu übergeben oder ein Objekt in eine binäre Datenstruktur zu speichern, um es später abrufen zu können. Eine Objektadresse kann per x := &obj
abgerufen werden; wenn die Variable obj allerdings geleert wird, könnte das Objekt vorzeitig freigegeben werden. Mit ObjAddRef, wie oben gezeigt, oder mit der Object()
-Funktion, wie unten gezeigt, kann sichergestellt werden, dass so etwas nicht passiert:
Adresse := Object(Objekt)-
Darüber hinaus kann man mit dieser Funktion die Adresse wieder in ein Verweis umwandeln:
+In einigen seltenen Fällen kann es erforderlich sein, ein Objekt per DllCall an einen externen Code zu übergeben oder ein Objekt in eine binäre Datenstruktur zu speichern, um es später abrufen zu können. Die Adresse eines Objekts kann via Adresse := &Objekt
abgerufen werden; dies würde allerdings effektiv zwei Verweise auf das Objekt erzeugen, aber das Programm kennt nur den einen in Objekt. Wenn der letzte bekannte Verweis auf das Objekt freigegeben wurde, wird das Objekt gelöscht. Demzufolge muss das Skript das Objekt darüber informieren, dass es einen Verweis erhalten hat. Es gibt zwei Wege, um das zu erreichen:
; Methode #1: Referenzzählung explizit erhöhen. +Adresse := &Objekt +ObjAddRef(Adresse) + +; Methode #2: Object() benutzen, das die Referenzzählung erhöht und eine Adresse zurückgibt. +Adresse := Object(Objekt)+
Mit dieser Funktion kann man auch eine Adresse wieder in ein Verweis umwandeln:
Objekt := Object(Adresse)-
In jedem Fall wird die Referenzzählung des Objekts automatisch erhöht, um ein vorzeitiges Freigeben des Objekts zu verhindern.
-Beachte, dass diese Funktionen ebenso für Objekte gelten, die nicht mit Object() erstellt wurden, wie COM-Objekt-Wrapper oder File-Objekte.
+In jedem Fall muss das Skript das Objekt außerdem darüber informieren, dass es mit diesem Verweis fertig ist:
+; Verringert die Referenzzählung des Objekts, damit es freigegeben werden kann: +ObjRelease(Adresse) ++
Generell sollte jede neue Kopie einer Objektadresse als seperater Objektverweis behandelt werden, demzufolge sollte das Skript ObjAddRef aufrufen, wenn es eine Kopie erhält, und sofort ObjRelease aufrufen, bevor es eine verliert. Würde man beispielsweise eine Adresse mit so etwas wie x := Adresse
kopieren, sollte ObjAddRef aufgerufen werden. Ebenso sollte das Skript ObjRelease aufrufen, wenn es mit x fertig ist (oder dabei ist den Wert von x zu überschreiben).
Beachte, dass die Object()-Funktion sogar auf Objekte angewendet werden kann, die sie selbst nicht erstellt hat, wie z. B. COM-Objekt-Wrapper oder File-Objekte.