RegisterCallback() [v1.0.47+]

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

Adresse := RegisterCallback("FunktionName" , Optionen := "", ParamAnzahl := FormaleAnzahl, EventInfo := Adresse)

Parameter

Adresse

Bei Erfolg gibt RegisterCallback() eine numerische Adresse zurück, die mit DllCall() oder Ähnlichem aufgerufen werden kann. Bei Misserfolg gibt sie eine leere Zeichenkette zurück. Fehler treten auf, wenn FunktionName: 1) nicht existiert; 2) zu viele oder zu wenig Parameter in Bezug auf ParamAnzahl akzeptiert; oder 3) einen beliebigen ByRef-Parameter akzeptiert.

FunktionName

Der Name einer Funktion, der in Anführungszeichen gesetzt werden muss, wenn er eine direkt geschriebene Zeichenkette ist. Diese Funktion wird jedes Mal automatisch aufgerufen, wenn Adresse aufgerufen wird. Die Funktion erhält auch die Parameter, die an Adresse übergeben wurden.

[v1.1.06+]: Anstelle eines Funktionsnamens kann eine Funktionsreferenz übergeben werden.

Optionen

Geben Sie keine oder mehr der folgenden Wörter an. Trennen Sie diese Optionen jeweils mit einem Leerzeichen (z.B. C Fast).

Fast oder F: Verhindert, dass bei jedem Aufruf von FunktionName ein neuer Thread gestartet wird. Trotz der daraus resultierenden höheren Leistung sollte diese Option nicht verwendet werden, wenn der Thread, in dem Adresse aufgerufen wird, variiert (z. B. wenn das Callback durch eine eingehende Meldung ausgelöst wurde). Der Grund dafür ist, dass FunktionName in der Lage sein wird, globale Einstellungen wie ErrorLevel, A_LastError und das zuletzt gefundene Fenster eines Threads zu ändern, der zum Zeitpunkt des Aufrufs aktiv war. Weitere Informationen finden Sie unter Bemerkungen.

CDecl oder C: Macht Adresse konform mit der "C"-Aufrufkonvention. Diese Option lässt man üblicherweise weg, weil die meisten Callbacks die Standardaufrufkonvention verwenden.

ParamAnzahl

Die Anzahl der Parameter, die der Aufrufer von Adresse übergeben muss. Lässt man diesen Parameter weg, wird standardmäßig die Anzahl der Pflichtparameter verwendet, die in der Definition von FunktionName angegeben sind. In beiden Fällen muss sichergestellt werden, dass der Aufrufer genau diese Anzahl von Parametern übergibt.

EventInfo

Ein Integer, den FunktionName in A_EventInfo sehen wird, wenn die Funktion über diese Adresse aufgerufen wird. Diese Option ist nützlich, wenn FunktionName von mehr als einer Adresse aufgerufen wird. Lässt man diesen Parameter weg, wird standardmäßig Adresse verwendet. Hinweis: Im Gegensatz zu anderen globalen Einstellungen wird A_EventInfo des aktuellen Threads nicht vom Fast-Modus beeinflusst.

Wenn zum Ausführen des Skripts die 32-Bit-Version von AutoHotkey verwendet wird, muss dieser Parameter ein Integer im Bereich von 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, unterstützt AutoHotkey nur bedingt vorzeichenlose 64-Bit-Integer, demzufolge könnten einige Operationen bewirken, dass der Wert in den vorzeichenfähigen Bereich rutscht.

Die Parameter der Callback-Funktion

Eine Funktion, die einer Callback-Adresse zugewiesen wurde, kann bis zu 31 Parameter akzeptieren. Optionale Parameter sind erlaubt; dies ist nützlich, wenn die 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 Callbacks aus Integern besteht und dass einige Neuinterpretationen gemacht werden müssen.

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

Wenn ein eingehender Parameter als vorzeichenfähiger Integer vorgesehen ist, können negative Zahlen mit einem der folgenden Beispiele offenbart werden:

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

; Methode #2: Verlässt sich auf die Tatsache, dass AutoHotkey von sich aus vorzeichenfähige 64-Bit-Integer verwendet.
wParam := wParam << 32 >> 32

AutoHotkey 64-Bit: Alle eingehenden Parameter sind vorzeichenfähige 64-Bit-Integer. AutoHotkey unterstützt von sich aus keine vorzeichenlose 64-Bit-Integers. 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) vorgesehen ist, sind die höheren Bits des Wertes willkürlich gesetzt - um diese herauszufiltern, können Sie wie folgt das bitweise UND nutzen:

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

Wenn ein eingehender Parameter von seinem Aufrufer als Zeichenkette vorgesehen 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 die Funktion als variadisch deklariert ist, bekommt ihr letzter Parameter die Adresse des ersten Callback-Parameters zugewiesen, der keinem Skriptparameter zugewiesen wurde. Zum Beispiel:

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

Die meisten Callbacks 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.

Bei Cdecl- oder 64-Bit-Aufrufkonventionen hat ParamAnzahl nur Einfluss darauf, wie vielen Skriptparametern Werte zugewiesen werden. Lässt man diesen Parameter weg, bekommen alle optionalen Parameter ihren Standardwert zugewiesen, außerdem werden sie von der Berechnung der in params gespeicherten Adresse ausgeschlossen.

Was die Funktion per Return zurückgeben sollte

Wenn die Funktion ein Return ohne Parameter verwendet oder einen leeren Wert wie "" zurückgibt (oder überhaupt kein Return verwendet), wird 0 an den Aufrufer des Callbacks zurückgegeben. Ansonsten sollte die Funktion einen Integer zurückgeben, der dann an den Aufrufer zurückgegeben wird. AutoHotkey 32-Bit kürzt Rückgabewerte auf das 32-Bit-Format, während AutoHotkey 64-Bit Rückgabewerte im 64-Bit-Format unterstützt. Zurückgegebene Strukturen größer als diesen Wert werden nicht unterstützt.

Fast vs. Slow

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

Der Fast-Modus hingegen erbt die globalen Einstellungen von einem Thread, der zum Zeitpunkt des Funktionsaufrufs aktiv war. Darüber hinaus werden jegliche Änderungen, die die Funktion an den globalen Einstellungen (einschließlich ErrorLevel und dem zuletzt gefundenen Fenster) vornimmt, für den aktuellen Thread wirksam. Demzufolge sollte der Fast-Modus nur verwendet werden, wenn genau bekannt ist, in welchem oder welchen Threads die Funktion aufgerufen wird.

Um zu verhindern, dass ein Callback sich selbst unterbricht (oder von einem anderen Thread unterbrochen wird), können Sie Critical in die erste Zeile einfügen. Allerdings ist diese Vorgehensweise nicht besonders effektiv, wenn die Funktion indirekt über eine eingehende Meldung kleiner als 0x0312 aufgerufen wird (das Intervall von Critical zu erhöhen könnte hierbei helfen). Darüber hinaus hindert Critical die Funktion nicht daran, etwas zu tun, das zum 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 beim Beenden des Skripts freigibt, muss jedes Skript, das Speicher für eine kleine feste Anzahl von Callbacks reserviert hat, den Speicher nicht explizit freigegeben. Ein Skript hingegen, das RegisterCallback() unbestimmt/unbegrenzt oft aufruft, sollte folgende Anweisung bei jedem unbenutzten Callback aufrufen:

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

DllCall(), OnMessage(), OnExit(), OnExit, OnClipboardChange(), OnClipboardChange-Label, Sort-Callback, Critical, Post/SendMessage, Funktionen, Liste mit Fenstermeldungen, Threads

Beispiele

Zeigt eine Zusammenfassung aller nicht-untergeordneten Fenster an.

; Aus Leistungs- und Speichergründen wird RegisterCallback() nur einmal für einen bestimmten Callback aufgerufen:
if not EnumAdresse  ; Fast-Modus ist okay, weil er nur von diesem Thread aufgerufen wird:
    EnumAdresse := RegisterCallback("EnumWindowsProc", "Fast")

DetectHiddenWindows On  ; Aufgrund des Fast-Modus wird diese Einstellung auch für Callback wirksam.

; Übergibt die Kontrolle an EnumWindows(), das Callback wiederholt aufruft:
DllCall("EnumWindows", "Ptr", EnumAdresse, "Ptr", 0)
MsgBox %Ausgabe%  ; Zeigt die gesammelten Informationen von Callback an.
    
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  ; Setzt EnumWindows() fort, bis alle Fenster abgearbeitet wurden.
}

Demonstriert, wie ein GUI-Fenster zu einer Unterklasse gemacht werden kann, wenn man dessen WindowProc an einen neuen WindowProc weiterleitet. 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 einer`nbenutzerdefinierter Hintergrundfarbe.
Gui +LastFound
GuiHwnd := WinExist()

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

WindowProcNeu := RegisterCallback("WindowProc", ""  ; Mit "" wird der Fast-Modus beim Setzen der Unterklasse verhindert.
    , , 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  ; Gibt HBRUSH zurück, um dem OS mitzuteilen, dass wir HDC geändert haben.
    }
    ; Ansonsten (da oben nichts zurückgegeben wurde) werden alle unbehandelten Ereignisse an das originale WindowProc übergeben.
    return DllCall("CallWindowProc", "Ptr", WindowProcAlt, "Ptr", hwnd, "UInt", uMsg, "Ptr", wParam, "Ptr", lParam)
}

GuiClose:
ExitApp