RegisterCallback() [v1.0.47+]

Erstellt eine Maschinencode-Adresse, die, wenn aufgerufen, den Aufruf an eine Funktion im Skript weiterleitet.

Adresse := RegisterCallback(Funktion , Optionen, ParamAnzahl, EventInfo)

Parameter

Funktion

Ein Funktionsname oder in [v1.1.06+] ein Funktionsobjekt, der/das aufgerufen werden soll. Um einen Funktionsnamen zu übergeben, setzen Sie ihn in Anführungszeichen. Diese Funktion wird jedes Mal automatisch aufgerufen, wenn Adresse aufgerufen wird. Die Funktion empfängt auch die Parameter, die an Adresse übergeben wurden.

Optionen

Wenn leer oder weggelassen, wird jedes Mal, wenn Funktion aufgerufen wird, ein neuer Thread gestartet, und die Standardaufrufkonvention verwendet. Andernfalls geben Sie eine oder mehrere der folgenden Optionen an. Trennen Sie alle Optionen jeweils durch ein Leerzeichen (z.B. C Fast).

Fast oder F: Verhindert, dass bei jedem Aufruf von Funktion ein neuer Thread gestartet wird. Trotz der daraus resultierenden besseren Performanz sollte diese Option nicht verwendet werden, wenn der Thread, von dem aus Adresse aufgerufen wird, variiert (z.B. wenn der Rückruf durch eine eingehende Meldung ausgelöst wurde). Der Grund dafür ist, dass Funktion globale Einstellungen wie ErrorLevel, A_LastError und das zuletzt gefundene Fenster für jeden Thread ändern kann, der zum Zeitpunkt des Aufrufs gerade läuft. Weitere Informationen finden Sie unter Bemerkungen.

CDecl oder C: Macht Adresse konform zur "C"-Aufrufkonvention. Dies wird normalerweise weggelassen, da die Standardaufrufkonvention für Rückrufe weitaus gebräuchlicher ist.

ParamAnzahl

Wenn leer oder weggelassen, wird standardmäßig die Anzahl der Pflichtparameter in der Definition von Funktion verwendet. Andernfalls geben Sie die Anzahl der Parameter an, die der Aufrufer von Adresse übergeben wird. In beiden Fällen muss sichergestellt werden, dass der Aufrufer genau diese Anzahl von Parametern übergibt.

EventInfo

Wenn weggelassen, wird standardmäßig Adresse verwendet. Wenn leer, wird standardmäßig 0 verwendet. Andernfalls geben Sie einen Integer an, den Funktion in A_EventInfo sehen wird, wenn die Funktion über diese Adresse aufgerufen wird. Dies ist nützlich, wenn Funktion von mehr als einer Adresse aufgerufen wird. Hinweis: Im Gegensatz zu anderen globalen Einstellungen wird A_EventInfo des aktuellen Threads nicht durch den langsamen Modus beeinträchtigt.

Wenn die 32-Bit-Version von AutoHotkey verwendet wird, um das Skript auszuführen, muss dieser Parameter ein Integer zwischen 0 und 4294967295 sein. Wenn die 64-Bit-Version von AutoHotkey verwendet wird, kann dieser Parameter ein 64-Bit-Integer sein. Obwohl A_EventInfo üblicherweise einen vorzeichenlosen Integer zurückgibt, bietet AutoHotkey keine vollständige Unterstützung für vorzeichenlose 64-Bit-Integer, weshalb einige Operationen dazu führen können, dass der Wert in den vorzeichenfähigen Bereich fällt.

Rückgabewert

Bei Erfolg gibt RegisterCallback() eine numerische Adresse zurück, die mit DllCall() o.ä. aufgerufen werden kann. Bei Misserfolg gibt sie eine leere Zeichenkette zurück. Misserfolg tritt auf, wenn Funktion: 1) nicht existiert; 2) zu viele oder zu wenig Parameter gemäß ParamAnzahl akzeptiert; oder 3) einen ByRef-Parameter akzeptiert.

Die Parameter von Funktion

Eine Funktion, die einer Rückrufadresse zugewiesen wurde, akzeptiert bis zu 31 Parameter. Optionale Parameter sind erlaubt; dies ist nützlich, wenn Funktion von mehr als einem Aufrufer aufgerufen wird.

Die korrekte Interpretation der Parameter erfordert ein gewisses Verständnis dafür, wie die x86-Aufrufkonventionen funktionieren. Da Autohotkey keine typisierten Parameter besitzt, wird davon ausgegangen, dass die Parameterliste des Rückrufs aus Integern besteht, was eine gewisse Neuinterpretation zur Folge haben kann.

AutoHotkey 32-Bit: Alle eingehenden Parameter sind vorzeichenlose 32-Bit-Integer. Kleinere Typen werden auf 32 Bit erweitert, während größere Typen in mehrere 32-Bit-Parameter aufgeteilt werden.

Wenn ein eingehender Parameter als vorzeichenfähiger Integer gedacht ist, können negative Zahlen mit einer der folgenden Methoden enthüllt werden:

; Methode #1
if (wParam > 0x7FFFFFFF)
    wParam := -(~wParam) - 1

; Methode #2: Verlässt sich darauf, dass AutoHotkey nativ vorzeichenfähige 64-Bit-Integer verwendet.
wParam := wParam << 32 >> 32

AutoHotkey 64-Bit: Alle eingehenden Parameter sind vorzeichenfähige 64-Bit-Integer. Nativ unterstützt AutoHotkey keine vorzeichenlose 64-Bit-Integer. Kleinere Typen werden auf 64 Bit erweitert, während größere Typen immer via Adresse übergeben werden.

AutoHotkey 32-Bit/64-Bit: Wenn ein eingehender Parameter als 8-Bit- oder 16-Bit-Wert (oder 32-Bit in x64) gedacht ist, können die höheren Bits des Wertes "Müll" enthalten, der aber mittels bitweisem UND herausgefiltert werden kann. Zum Beispiel:

Rückruf(UCharParam, UShortParam, UIntParam) {
    UCharParam &= 0xFF
    UShortParam &= 0xFFFF
    UIntParam &= 0xFFFFFFFF
    ;...
}

Wenn ein eingehender Parameter von seinem Aufrufer als Zeichenkette gedacht ist, dann ist das, was tatsächlich ankommt, die Adresse der Zeichenkette. Mit StrGet() kann die Zeichenkette selbst abgerufen werden:

MeineZkette := StrGet(MeinParameter)  ; Benötigt [AHK_L 46+]

Wenn ein eingehender Parameter die Adresse einer Struktur ist, können die einzelnen Elemente durch Befolgen der Schritte in DllCall-Strukturen extrahiert werden.

Parameter via Adresse empfangen [AHK_L 60+]: Wenn Funktion als variadisch deklariert ist, bekommt ihr letzter Parameter die Adresse des ersten Rückrufparameters zugewiesen, der keinem Skriptparameter zugewiesen wurde. Zum Beispiel:

Rückruf := RegisterCallback("DieFunk", "F", 3)  ; Größe der Parameterliste muss angegeben werden.
DieFunk("DieFunk wurde direkt aufgerufen")  ; DieFunk direkt aufrufen.
DllCall(Rückruf, "float", 10.5, "int64", 42)  ; DieFunk via Rückruf aufrufen.
DieFunk(params*) {
    if IsObject(params)
        MsgBox % params[1]
    else
        MsgBox % NumGet(params+0, "float") ", " NumGet(params+A_PtrSize, "int64")
}

Die meisten Rückrufe verwenden die stdcall-Aufrufkonvention, die eine feste Anzahl von Parametern voraussetzt. In solchen Fällen muss ParamAnzahl auf die Größe der Parameterliste gesetzt werden, wobei Int64 und Double als zwei 32-Bit-Parameter zählen.

In Verbindung mit Cdecl oder der 64-Bit-Aufrufkonvention hat ParamAnzahl nur Einfluss darauf, wie vielen Skriptparametern Werte zugewiesen werden. Wenn weggelassen, werden alle optionalen Parameter ihre Standardwerte erhalten und von den Berechnungen für die in params gespeicherte Adresse ausgeschlossen.

Was Funktion zurückgeben muss

Wenn Funktion ein Return verwendet, dessen Parameter leer ist oder weggelassen wird, oder überhaupt kein Return verwendet, wird 0 an den Aufrufer des Rückrufs zurückgegeben. Andernfalls muss Funktion einen Integer zurückgeben, der dann an den Aufrufer zurückgegeben wird. Die 32-Bit-Version von AutoHotkey kürzt Rückgabewerte auf 32 Bit, während die 64-Bit-Version von AutoHotkey 64-Bit-Rückgabewerte unterstützt. Die Rückgabe von Strukturen größer als diese (nach Wert) wird nicht unterstützt.

Schnell vs. Langsam

Der reguläre/langsame Modus bewirkt, dass Funktion vorerst die Standardwerte von Einstellungen wie SendMode und DetectHiddenWindows verwendet. Diese Standardwerte können im automatischen Ausführungsbereich geändert werden.

Der schnelle Modus hingegen erbt die globalen Einstellungen von jedem Thread, der zum Zeitpunkt des Aufrufs von Funktion gerade läuft. Außerdem werden alle Änderungen, die Funktion an den globalen Einstellungen (einschließlich ErrorLevel und dem zuletzt gefundenen Fenster) vornimmt, für den aktuellen Thread wirksam. Folglich sollte der schnelle Modus nur verwendet werden, wenn genau bekannt ist, in welchem Thread Funktion aufgerufen wird.

Um eine Unterbrechung durch sich selbst (oder einen anderen Thread) zu vermeiden, kann ein Rückruf Critical in seiner ersten Zeile verwenden. Dies ist jedoch nicht sehr effektiv, wenn Funktion indirekt über eine eingehende Meldung kleiner als 0x0312 aufgerufen wird (eine Erhöhung des Critical-Intervalls könnte helfen). Außerdem wird Funktion durch Critical nicht daran gehindert, etwas zu tun, was zu einem indirekten Selbstaufruf führen könnte, wie z.B. der Aufruf von SendMessage oder DllCall().

Speicher

Jedes Mal, wenn RegisterCallback() verwendet wird, wird eine kleine Menge an Speicher reserviert (32 Bytes plus System-Overhead). Da das Betriebssystem diesen Speicher automatisch freigibt, wenn das Skript beendet wird, ist es nicht notwendig, dass ein Skript, das Speicher für eine kleine feste Anzahl von Rückrufen reserviert hat, den Speicher explizit freigibt. Im Gegensatz dazu sollte ein Skript, das RegisterCallback() unbestimmt/unbegrenzt oft aufruft, die folgende Anweisung für jeden unbenutzten Rückruf aufrufen:

DllCall("GlobalFree", "Ptr", Adresse, "Ptr")

DllCall(), OnMessage(), OnExit, OnClipboardChange, Sort-Rückruffunktion, Critical, Post/SendMessage, Funktionen, Windows-Meldungen, Threads

Beispiele

Zeigt eine Zusammenfassung aller Top-Level-Fenster an.

; RegisterCallback() aus Performanz- und Speichergründen nur einmal für einen bestimmten Rückruf aufrufen:
if not EnumAdresse  ; Schneller Modus ist okay, da es nur von diesem Thread aufgerufen wird:
    EnumAdresse := RegisterCallback("EnumWindowsProc", "Fast")

DetectHiddenWindows On  ; Durch den schnellen Modus wird diese Einstellung auch für den Rückruf wirksam.

; Kontrolle an EnumWindows() übergeben, das den Rückruf wiederholt aufruft:
DllCall("EnumWindows", "Ptr", EnumAdresse, "Ptr", 0)
MsgBox %Ausgabe%  ; Die vom Rückruf gesammelten Informationen anzeigen.
    
EnumWindowsProc(hwnd, lParam)
{
    global Ausgabe
    WinGetTitle, Titel, ahk_id %hwnd%
    WinGetClass, Klasse, ahk_id %hwnd%
    if Titel
        Ausgabe .= "HWND: " . hwnd . "`tTitel: " . Titel . "`tKlasse: " . Klasse . "`n"
    return true  ; EnumWindows() fortfahren lassen, bis alle Fenster enumeriert wurden.
}

Zeigt, wie ein GUI-Fenster zu einer Unterklasse gemacht werden kann, indem dessen WindowProc an ein neues WindowProc weitergeleitet wird. In diesem Fall wird die Hintergrundfarbe des Text-Steuerelements auf eine benutzerdefinierte Farbe gesetzt.

TextHintergrundFarbe := 0xFFBBBB  ; Eine benutzerdefinierte Farbe im BGR-Format.
TextHintergrundPinsel := DllCall("CreateSolidBrush", "UInt", TextHintergrundFarbe)

Gui, Add, Text, HwndMeinTextHwnd, Hier ein Text mit`nbenutzerdefinierter Hintergrundfarbe.
Gui +LastFound
GuiHwnd := WinExist()

; 64-Bit-Skripte müssen SetWindowLongPtr statt SetWindowLong aufrufen:
SetWindowLong := A_PtrSize=8 ? "SetWindowLongPtr" : "SetWindowLong"

WindowProcNeu := RegisterCallback("WindowProc", ""  ; "" angeben, um den schnellen Modus beim Setzen der Unterklasse zu verhindern.
    , , MeinTextHwnd)  ; In [v1.1.12+] kann ParamAnzahl wie hier gezeigt weggelassen werden.
WindowProcAlt := DllCall(SetWindowLong, "Ptr", GuiHwnd, "Int", -4  ; -4 ist GWL_WNDPROC
    , "Ptr", WindowProcNeu, "Ptr") ; Rückgabewert muss auf Ptr oder UPtr statt auf Int gesetzt werden.

Gui Show
return

WindowProc(hwnd, uMsg, wParam, lParam)
{
    Critical
    global TextHintergrundFarbe, TextHintergrundPinsel, WindowProcAlt
    if (uMsg = 0x0138 && lParam = A_EventInfo)  ; 0x0138 ist WM_CTLCOLORSTATIC.
    {
        DllCall("SetBkColor", "Ptr", wParam, "UInt", TextHintergrundFarbe)
        return TextHintergrundPinsel  ; HBRUSH zurückgeben, um dem System mitzuteilen, dass wir HDC geändert haben.
    }
    ; Andernfalls (da oben nichts zurückgegeben wurde) alle unbehandelten Ereignisse an das originale WindowProc übergeben.
    return DllCall("CallWindowProc", "Ptr", WindowProcAlt, "Ptr", hwnd, "UInt", uMsg, "Ptr", wParam, "Ptr", lParam)
}

GuiClose:
ExitApp