OnMessage()

Bestimmt eine Funktion oder ein Funktionsobjekt, die automatisch aufgerufen werden sollen, wenn das Skript die angegebene Meldung empfängt.

OnMessage(Meldenummer [, Funktion, MaxThreads])

Parameter

Meldenummer

Die Nummer einer Meldung zwischen 0 und 4294967295 (0xFFFFFFFF), die überwacht oder abgefragt werden soll. Sofern das Überwachen einer Systemmeldung nicht erforderlich ist (also eine unter 0x400), sollte man am besten eine Nummer verwenden, die größer ist als 4096 (0x1000). Diese Methode verringert die Chance, dass interne Meldungen von AutoHotkey gestört werden.

Funktion

Der Name einer Funktion, oder seit [v1.1.20+] ein Funktionsobjekt. Um einen literalen Funktionsnamen zu übergeben, muss er in Anführungszeichen gesetzt werden.

Wie die Funktion registriert wird und welcher Rückgabewert von OnMessage erfolgt, hängt davon ab, ob dieser Parameter eine Zeichenkette oder ein Funktionsobjekt ist. Einzelheiten findest du unter Funktionsname vs. Objekt.

MaxThreads [v1.0.47+]

Normalerweise lässt man diesen Parameter (Integer) weg, um die überwachende Funktion jeweils auf einen einzigen Thread zu begrenzen. Diese Methode ist üblicherweise am sinnvollsten, weil das Skript sonst Meldungen in chronologisch falscher Reihenfolge abarbeiten würde, wann immer die überwachende Funktion sich selbst unterbricht. Demzufolge kann Critical als Alternative zu MaxThreads verwendet werden, wie es weiter unten beschrieben wird.

[v1.1.20+]: Standardmäßig werden mehrere Funktionen, die für eine einzige Meldenummer registriert sind, in der Reihenfolge aufgerufen, wie sie registriert wurden. Um eine Funktion zu registrieren, die vor allen anderen registrierten Funktionen erfolgen soll, muss ein negativer Wert bei MaxThreads angegeben werden. Zum Beispiel würde OnMessage(Meld, Fn,-2) die Funktion Fn vor allen anderen registrierten Funktionen für Meld aufrufen, und maximal 2 Threads für Fn erlauben. Wenn die Funktion allerdings bereits registriert ist, wird die Reihenfolge nicht geändert, sofern sie nicht de- und dann neuregistriert wurde.

Funktionname vs. Objekt

OnMessage's Rückgabewert und Verhalten sind abhängig davon, ob der Funktionsparameter ein Funktionsname oder ein Objekt ist.

Funktionsname

Aus Gründen der Abwärtskompatibilität kann höchstens eine Funktion via Name registriert werden, um jede einzigartige Meldenummer zu überwachen -- dies wird auch als "Legacy"-Überwachung bezeichnet.

Wenn die Legacy-Überwachung das erste Mal registriert wird, ist die Tatsache, ob sie vor oder nach zuvor registrierten Überwachungen aufgerufen wird, abhängig vom MaxThreads-Parameter. Die Überwachung dahingehend zu ändern, eine andere Funktion aufzurufen, beeinflusst nicht die Reihenfolge, sofern die Überwachung nicht erst deregistriert wird.

Folgendes registriert oder ändert die Legacy-Überwachung für Meldenummer (lass die Anführungszeichen weg, wenn du eine Variable übergeben willst):

Name := OnMessage(Meldenummer, "Funktionsname")

Der Rückgabewert ist einer der folgenden:

Folgendes deregistriert die aktuelle Legacy-Überwachung für Meldenummer (falls vorhanden) und gibt ihren Namen zurück (leer, wenn sie keinen hat):

Name := OnMessage(Meldenummer, "")

Folgendes gibt den Namen der aktuellen Legacy-Überwachung für Meldenummer zurück (leer, wenn sie keinen hat):

Name := OnMessage(Meldenummer)

Funktionsobjekt

Beliebig viele Funktionsobjekte (einschließlich normale Funktionen) können die angegebene Meldenummer überwachen.

Beide folgenden Zeilen registrieren ein Funktionsobjekt, das nach allen zuvor registrierten Funktionen aufgerufen wird:

OnMessage(Meldenummer, FunkObj)     ; Option 1
OnMessage(Meldenummer, FunkObj, 1)  ; Option 2 (MaxThreads = 1)

Folgendes registriert ein Funktionsobjekt, das vor allen zuvor registrierten Funktionen aufgerufen wird:

OnMessage(Meldenummer, FunkObj, -1)

Um ein Funktionsobjekt zu deregistrieren, muss 0 bei MaxThreads angegeben werden:

OnMessage(Meldenummer, FunkObj, 0)

Misserfolg

Misserfolg tritt auf, wenn Funktion:

  1. kein Objekt ist, kein Name einer benutzerdefinierten Funktion ist, oder eine leere Zeichenkette ist;
  2. wissentlich mehr als vier Parameter benötigt; oder
  3. in v1.0.48.05 oder älter ByRef- oder optionale Parameter hat.

In v1.1.19.03 oder älter tritt Misserfolg auch auf, wenn das Skript versucht, eine neue Meldung zu überwachen, während bereits 500 Meldungen überwacht werden.

Wenn Funktion ein Objekt ist, wird bei Misserfolg eine Ausnahme ausgelöst. Ansonsten wird eine leere Zeichenkette zurückgegeben.

Die Parameter der Funktion

Eine Funktion, die in OnMessage zum Überwachen von einer oder mehreren Meldungen angegeben wurde, akzeptiert bis zu vier Parameter:

MeineMeldeüberwachung(wParam, lParam, msg, hwnd)
{
    ... Funktionsbereich ...
}

Zwar können die Parameter einen beliebigen Namen haben, allerdings müssen sie in einer bestimmten Reihenfolge zugewiesen werden:

Parameter #1: WPARAM-Wert der Meldung.
Parameter #2: LPARAM-Wert der Meldung.
Parameter #3: Meldungsnummer, die dort gebraucht wird, wo eine Funktion mehr als eine Meldung überwacht.
Parameter #4: HWND (eindeutige ID) des Fensters oder Steuerelements, zu dem die Meldung gesendet wurde. Das HWND kann in Verbindung mit ahk_id verwendet werden.

WPARAM und LPARAM sind vorzeichenlose 32-Bit-Integer (von 0 bis 232-1) oder vorzeichenbehaftete 64-Bit-Integer (von -263 bis 263-1), abhängig davon, ob die skript-ausführende EXE-Datei 32- oder 64-Bit ist. Wenn bei einem 32-Bit-Skript ein eingehender Parameter als vorzeichenbehafteter Integer vorgesehen ist, können negative Zahlen zum Beispiel wie folgt offenbart werden:

if (A_PtrSize = 4 && wParam > 0x7FFFFFFF)  ; Das Überprüfen von A_PtrSize stellt sicher, dass das Skript 32-Bit ist.
    wParam := -(~wParam) - 1

Du kannst eine oder mehrere Parameter vom Ende der Liste weglassen, wenn du die entsprechende Information nicht benötigst. Zum Beispiel würde eine Funktion, definiert als MeineMeldeüberwachung(wParam, lParam), nur die ersten zwei Parameter empfangen, und eine, definiert als MeineMeldeüberwachung(), gar keine von ihnen.

Weitere verfügbare Informationen für die Funktion

Neben den oben genannten Parametern kann die Funktion auch auf die Werte der folgenden Built-in-Variablen zurückgreifen:

Das zuletzt gefundene Fenster einer überwachenden Funktion ist zu Beginn das übergeordnete Fenster, zu dem die Meldung gesendet wurde (selbst wenn sie zu einem Steuerelement gesendet wurde). Ist das Fenster versteckt, aber kein GUI-Fenster (z. B. das Hauptfenster des Skripts), muss zuvor DetectHiddenWindows aktiviert werden. Zum Beispiel:

DetectHiddenWindows On
MeldÜbergeordFenster := WinExist()  ; Dies speichert die eindeutige ID des Fensters, zu dem die Meldung gesendet wurde.

Was die Funktion per Return zurückgeben sollte

Verwendet eine überwachende Funktion Return ohne Parameter oder mit einem leeren Wert wie "" (oder verwendet sie überhaupt kein Return), wird die eingehende Meldung beim Erreichen des Funktionsendes normal fortgesetzt. Das gleiche passiert, wenn man das Ende der Funktion via Exit erzwingt oder einen Laufzeitfehler verursacht (der beispielsweise durch Ausführen einer nicht-existierenden Datei ausgelöst werden kann). Vewendet man als Rückgabewert hingegen einen Integer, wird dieser Wert sofort als Antwort gesendet; das heißt, dass das Programm die Meldung nicht weiter fortsetzen wird. Zum Beispiel könnte eine Funktion, die WM_LBUTTONDOWN (0x201) überwacht, einen Integer zurückgeben, um das Zielfenster daran zu hindern, benachrichtigt zu werden, dass ein Mausklick erfolgt ist. In vielen Fällen (z. B. als eine Meldung, empfangen via PostMessage) ist es egal, welcher Integer zurückgegeben wird; aber im Zweifelsfall ist eine 0 am sichersten.

Der Bereich von gültigen Rückgabewerten ist abhängig davon, ob die skript-ausführende EXE-Datei 32- oder 64-Bit ist. Bei einem 32-Bit-Skript (A_PtrSize = 4) müssen die Rückgabewerte in einem Bereich von -231 und 232-1 sein, und bei einem 64-Bit-Skript (A_PtrSize = 8) in einem Bereich von -263 und 263-1.

[v1.1.20+]: Mehrere Funktionen, die eine bestimmte Meldungsnummer überwachen, werden nacheinander aufgerufen, bis eine von denen einen nicht-leeren Wert zurückgibt.

Allgemeine Bemerkungen

Im Gegensatz zu einem normalen Funktionsaufruf wird die überwachende Funktion, wenn sie eine neue Meldung empfängt, als neuer Thread aufgerufen. Aus diesem Grund beginnt die Funktion mit den Standardeinstellungen bei Befehlen wie SendMode und DetectHiddenWindows. Diese Standardeinstellungen können im automatischen Ausführungsbereich geändert werden.

Meldungen, die via SendMessage (statt PostMessage) zu einem Steuerelement gesendet wurden, werden nicht überwacht, weil das Betriebssystem diese Meldungen direkt auf das Steuerelement überträgt, ohne dass jemand etwas mitbekommt. Für system-generierte Meldungen ist das selten ein Problem, weil die meisten von ihnen via PostMessage gesendet werden.

Jedes Skript, das OnMessage irgendwo aufruft, ist automatisch persistent. Außerdem darf es standardmäßig nur einmal vorkommen, sofern man das nicht mit #SingleInstance überschreibt.

Kommt eine Meldung an, während die Funktion noch läuft, wird die Funktion nicht erneut aufgerufen (außer wenn MaxThreads größer als 1 ist); stattdessen wird die Meldung so behandelt, als wurde sie nicht überwacht. Wenn dies nicht erwünscht ist, muss man Critical in der ersten Zeile der Funktion verwenden, um eine Meldung größer gleich 0x312 zwischenspeichern zu können. Alternativ könnte Thread Interrupt das gleiche bewirken, sofern die Funktion genug Zeit zum Beenden hat. Eine Meldung kleiner als 0x312 kann hingegen nicht via Critical oder "Thread Interrupt" zwischengespeichert werden (seit v1.0.46 kann Critical allerdings verwendet werden, um die Meldungen seltener überprüfen zu lassen, so dass die Funktion mehr Zeit zum Beenden hat). Um zu garantieren, dass solche Meldungen nicht verloren gehen, muss die Funktion innerhalb von 6 Millisekunden zum Ende kommen (dieses Limit kann mit Critical 30 erhöht werden). Eine Möglichkeit, dies zu tun, ist es, einen zukünftigen Thread in die Warteschlange zu setzen, indem man eine überwachte Meldungsnummer größer als 0x312 via PostMessage sendet. Die Funktion dieser Meldung sollte Critical in der ersten Zeile verwenden, um sicherzustellen, dass ihre Meldungen zwischengespeichert werden.

Kommt eine überwachte Meldung kleiner als 0x312 an, während das Skript absolut unterbrechungsfrei ist - z. B. während ein Menü angezeigt wird, KeyDelay/MouseDelay im Gange ist oder die Zwischenablage offen ist - wird die Funktion der Meldung nicht aufgerufen und die Meldung so behandelt, als wurde sie nicht überwacht. Im Gegensatz dazu wird eine überwachte Meldung größer gleich 0x312 während diesen unterbrechungsfreien Perioden zwischengespeichert; das heißt, dass ihre Funktion aufgerufen wird, wenn das Skript wieder unterbrochen werden kann.

Kommt eine überwachte Meldung kleiner als 0x312 an, während das Skript aufgrund von Thread Interrupt oder Critical unterbrechungsfrei ist, wird der aktuelle Thread unterbrochen, so dass die Funktion aufgerufen werden kann. Im Gegensatz dazu wird eine überwachte Meldung größer gleich 0x312 zwischengespeichert, bis der Thread endet oder wieder unterbrochen werden kann.

Die Priorität von OnMessage-Threads ist immer 0. Demzufolge werden keine Meldungen überwacht oder zwischengespeichert, wenn der aktuelle Thread eine Priorität höher als 0 hat.

Sei vorsichtig, wenn du Systemmeldungen (solche unter 0x400) überwachst. Kommt eine überwachende Funktion beispielsweise nicht schnell genug zum Ende, kann die Antwort auf die Meldung länger dauern als vom System erwartet, was zu Nebeneffekten führen könnte. Unerwünschtes Verhalten kann auch auftreten, wenn eine überwachende Funktion die Meldung durch Zurückgeben eines Integers nicht weiter fortsetzen lässt, während das System etwas anderes erwartet.

Zeigt das Skript ein Dialogfenster wie MsgBox an, werden Meldungen, die via PostMessage zu einem Steuerelement gesendet werden, nicht überwacht. Zeigt das Skript beispielsweise eine MsgBox an und klickt der Benutzer innerhalb eines GUI-Fensters auf einen Button, wird die WM_LBUTTONDOWN-Meldung direkt zum Button gesendet, ohne dass die überwachende Funktion aufgerufen wird.

Zwar kann ein externes Programm direkt via PostThreadMessage() oder anderen API-Funktionen Meldungen zu einem Skript-Thread senden, allerdings könnten dabei Meldungen verloren gehen, wenn das Skript ein Dialogfenster wie MsgBox anzeigt. Stattdessen ist es besser, Meldungen zum Hauptfenster des Skripts oder zu einem seiner GUI-Fenster zu senden.

Siehe auch

RegisterCallback(), OnExit, OnClipboardChange, Post/SendMessage, Funktionen, Liste mit Fenstermeldungen, Threads, Critical, DllCall()

Beispiele

; Beispiel: Das folgende Skript zeigt, wie Mausklicks innerhalb eines GUI-Fensters überwacht werden können.
; Verwandtes Thema: GuiContextMenu

Gui, Add, Text,, Klicken Sie irgendwo auf das Fenster.
Gui, Add, Edit, w200 vMeinEdit
Gui, Show
OnMessage(0x201, "WM_LBUTTONDOWN")
return

WM_LBUTTONDOWN(wParam, lParam)
{
    X := lParam & 0xFFFF
    Y := lParam >> 16
    if A_GuiControl
        Steuerelement := "`n(im Steuerelement " . A_GuiControl . ")"
    ToolTip Sie haben im GUI-Fenster #%A_Gui% auf die Koordinaten %X%x%Y% geklickt.%Steuerelement%
}

GuiClose:
ExitApp
; Beispiel: Das folgende Skript zeigt, wie man das Abmelden/Herunterfahren des Systems erkennen und unterbrechen kann (es wurde gemeldet, dass dies NICHT in Windows Vista und höher funktioniert).
; Verwandtes Thema: OnExit

; Der folgende DllCall-Befehl ist optional: er teilt dem OS mit, dass dieses Skript zuerst beendet werden soll (bevor alle anderen Anwendungen beendet werden).
DllCall("kernel32.dll\SetProcessShutdownParameters", UInt, 0x4FF, UInt, 0)
OnMessage(0x11, "WM_QUERYENDSESSION")
return

WM_QUERYENDSESSION(wParam, lParam)
{
    ENDSESSION_LOGOFF = 0x80000000
    if (lParam & ENDSESSION_LOGOFF)  ; Benutzer meldet sich ab.
        Ereignis = Abmelden
    else  ; System wird entweder heruntergefahren oder neu gestartet.
        Ereignis = Herunterfahren
    MsgBox, 4,, %Ereignis% im Gange.  Erlauben?
    IfMsgBox Yes
        return true  ; Erlaubt dem OS das Herunterfahren/Abmelden.
    else
        return false  ; Verbietet dem OS das Herunterfahren/Abmelden.
}
; Beispiel: Zeigt, wie ein Skript eine benutzerdefinierte Meldung und bis zu zwei Zahlen von einem anderen Skript oder Programm empfängt
; (Schaue dir das Beispiel danach an, wie man Zeichenketten sendet).

OnMessage(0x5555, "Meldeüberwachung")
OnMessage(0x5556, "Meldeüberwachung")

Meldeüberwachung(wParam, lParam, msg)
{
    ; Damit die Funktion schnell zum Ende kommen kann, ist es besser, ein ToolTip zu verwenden,
    ; weil so etwas wie eine MsgBox dies verhindern würde:
    ToolTip Meldung %msg% angekommen:`nWPARAM: %wParam%`nLPARAM: %lParam%
}

; Mit den folgenden Zeilen innerhalb eines anderen Skripts kann die Funktion im oberen Skript ausgeführt werden:
SetTitleMatchMode 2
DetectHiddenWindows On
if WinExist("Empfänger.ahk ahk_class AutoHotkey")
    PostMessage, 0x5555, 11, 22  ; Aufgrund von WinExist() wird die Meldung zum "zuletzt gefundenen Fenster" gesendet.
DetectHiddenWindows Off  ; Kann nach PostMessage wieder deaktiviert werden.
; Beispiel: Sendet eine Zeichenkette mit beliebiger Länge an ein anderes Skript.  Um dieses Beispiel
; verwenden zu können, musst du beide der folgenden Skripte speichern und ausführen und dann WIN+LEERTASTE drücken,
; um eine InputBox anzuzeigen, die den Benutzer zur Eingabe einer Zeichenkette auffordert.

; Speichere das folgende Skript als "Empfänger.ahk" und starte es:
#SingleInstance
OnMessage(0x4a, "Empfange_WM_COPYDATA")  ; 0x4a ist WM_COPYDATA
return

Empfange_WM_COPYDATA(wParam, lParam)
{
    ZeichenketteAdresse := NumGet(lParam + 2*A_PtrSize)  ; Ermittelt die Adresse des lpData-Elements in CopyDataStruct.
    Datenkopie := StrGet(ZeichenketteAdresse)  ; Kopiert die Zeichenkette aus der Struktur.
    ; Zeige die Zeichenkette via ToolTip statt MsgBox an, so dass wir rechtzeitig fertig werden:
    ToolTip %A_ScriptName%`nhat die folgende Zeichenkette empfangen:`n%Datenkopie%
    return true  ; Die Rückgabe einer 1 (wahr) ist der übliche Weg, um diese Meldung zu bestätigen.
}

; Speichere das folgende Skript als "Sender.ahk" und starte es:  Drücke danach den Hotkey WIN+LEERTASTE.
ZielSkriptTitel = Empfänger.ahk ahk_class AutoHotkey

#space::  ; Hotkey: WIN+LEERTASTE. Drücke ihn, um eine InputBox zur Eingabe einer Zeichenkette anzuzeigen.
InputBox, ZuSendendeZeichenkette, Text via WM_COPYDATA senden, Tragen Sie den zu sendenden Text ein:
if ErrorLevel  ; Benutzer hat den Abbrechen-Button gedrückt.
    return
Ergebnis := Sende_WM_COPYDATA(ZuSendendeZeichenkette, ZielSkriptTitel)
if Ergebnis = FAIL
    MsgBox SendMessage fehlgeschlagen. Gibt es den folgenden Fenstertitel?:`n%ZielSkriptTitel%
else if Ergebnis = 0
    MsgBox Meldung wurde gesendet, aber das Zielfenster hat mit 0 geantwortet, was bedeuten könnte, dass sie ignoriert wurde.
return

Sende_WM_COPYDATA(ByRef ZuSendendeZeichenkette, ByRef ZielSkriptTitel)  ; ByRef verbraucht in diesem Fall weniger Speicher.
; Diese Funktion sendet den angegebenen String zum angegebenen Fenster und gibt die Anwort zurück.
; Die Antwort ist 1, wenn das Zielfenster die Meldung akzeptiert hat, oder 0, wenn die Meldung ignoriert wurde.
{
    VarSetCapacity(KopieDatenStrukt, 3*A_PtrSize, 0)  ; Richtet den Speicherbereich der Struktur ein.
    ; Zuerst wird das cbData-Element der Struktur auf die Größe der Zeichenkette gesetzt, einschließlich dem Null-Terminator.
    GrößeInBytes := (StrLen(ZuSendendeZeichenkette) + 1) * (A_IsUnicode ? 2 : 1)
    NumPut(GrößeInBytes, KopieDatenStrukt, A_PtrSize)  ; Muss für das OS getan werden.
    NumPut(&ZuSendendeZeichenkette, KopieDatenStrukt, 2*A_PtrSize)  ; Lässt lpData auf die Zeichenkette selbst verweisen.
    Vorher_DetectHiddenWindows := A_DetectHiddenWindows
    Vorher_TitleMatchMode := A_TitleMatchMode
    DetectHiddenWindows On
    SetTitleMatchMode 2
    TimeoutWert = 4000  ; Optional. Zeit in Millisekunden, die Empfänger.ahk zum Antworten hat. Standard ist 5000
    ; Muss Send verwenden, nicht Post.
    SendMessage, 0x4a, 0, &KopieDatenStrukt,, %ZielSkriptTitel%,,,, %TimeoutWert% ; 0x4a ist WM_COPYDATA.
    DetectHiddenWindows %Vorher_DetectHiddenWindows%  ; Stellt die ursprüngliche Einstellung wieder her.
    SetTitleMatchMode %Vorher_TitleMatchMode%         ; Hier auch.
    return ErrorLevel  ; Gibt SendMessage's Antwort zum Aufrufer zurück.
}
; Beispiel: Siehe WinLIRC-Client-Skript, wie OnMessage() zum Empfangen von Meldungen verwendet
; werden kann, die über die Netzwerkverbindung gesendet wurden.