OnMessage

Registriert eine Funktion, die jedes Mal automatisch aufgerufen wird, wenn das Skript eine bestimmte Meldung empfängt.

OnMessage MldNummer, Rückruf , MaxThreads

Parameter

MldNummer

Typ: Integer

Die Nummer einer Meldung zwischen 0 und 4294967295 (0xFFFFFFFF), die überwacht oder abgefragt werden soll. Wenn Sie keine Systemmeldung (d.h. eine Meldung unter 0x0400) überwachen wollen, verwenden Sie am besten eine Zahl größer als 4096 (0x1000), falls möglich. Dies verringert das Risiko, dass Meldungen, die intern von aktuellen und zukünftigen AutoHotkey-Versionen verwendet werden, beeinträchtigt werden.

Rückruf

Typ: Funktionsobjekt

Eine Funktion, die aufgerufen werden soll.

Die Rückruffunktion akzeptiert vier Parameter und kann wie folgt definiert werden:

MeinRückruf(wParam, lParam, Mld, Hwnd) { ...

Es spielt keine Rolle, welche Namen Sie den Parametern geben, allerdings werden ihnen die folgenden Werte nacheinander zugewiesen:

  1. Der WPARAM-Wert der Meldung.
  2. Der LPARAM-Wert der Meldung.
  3. Die Meldungsnummer (nützlich, wenn eine Rückruffunktion mehr als eine Meldung überwacht).
  4. Die HWND-Nummer (eindeutige ID) des Fensters oder Steuerelements, das die Meldung empfangen hat. Die HWND-Nummer kann direkt in einem FensterTitel-Parameter verwendet werden.

Es können beliebig viele Parameter am Ende der Parameterliste der Rückruffunktion weggelassen werden, wenn die entsprechenden Informationen nicht benötigt werden, aber dann muss als letzter Parameter ein Sternchen angegeben werden, z.B. MeinRückruf(Param1, *).

WPARAM und LPARAM sind vorzeichenlose 32-Bit-Integer (von 0 bis 232-1) oder vorzeichenfähige 64-Bit-Integer (von -263 bis 263-1), abhängig davon, ob die zur Ausführung verwendete EXE-Datei 32-Bit oder 64-Bit ist. Wenn in einem 32-Bit-Skript ein eingehender Parameter als vorzeichenfähiger Integer vorgesehen ist, können negative Zahlen zum Beispiel wie folgt enthüllt werden:

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

Typ: Integer

Wenn weggelassen, wird standardmäßig 1 verwendet, d.h. die Rückruffunktion ist auf einen Thread beschränkt. Dies ist in der Regel am besten, weil das Skript sonst die Meldungen nicht in chronologischer Reihenfolge abarbeiten würde, wenn die Rückruffunktion sich selbst unterbricht. Daher sollten Sie als Alternative zu MaxThreads in Betracht ziehen, Critical wie unten beschrieben zu verwenden.

Wenn die Rückruffunktion direkt oder indirekt ein erneutes Senden der Meldung bewirkt, während die Rückruffunktion noch ausgeführt wird, muss ein MaxThreads-Wert größer als 1 oder kleiner als -1 angegeben werden, damit die Rückruffunktion für die neue Meldung aufgerufen werden kann (falls gewünscht). Meldungen, die vom Prozess des Skripts an sich selbst gesendet (nicht gepostet) werden, können nicht verzögert oder gepuffert werden.

Geben Sie 0 an, um die zuvor registrierte, via Rückruf identifizierte Rückruffunktion zu deregistrieren.

Wenn mehrere Rückruffunktionen für eine MldNummer registriert sind, werden diese standardmäßig in der Reihenfolge ihrer Registrierung aufgerufen. Um eine Rückruffunktion zu registrieren, die vor allen zuvor registrierten Rückruffunktionen aufgerufen wird, geben Sie einen negativen Wert für MaxThreads an. Zum Beispiel bewirkt OnMessage Mld, Fn, -2, dass Fn vor allen anderen Rückruffunktionen aufgerufen wird, die zuvor für Mld registriert wurden, und dass maximal 2 Threads für Fn möglich sind. Wenn die Rückruffunktion jedoch bereits registriert ist, bleibt die Reihenfolge unverändert, es sei denn, sie wird deregistriert und dann wieder registriert.

Verwendung

Es können beliebig viele Rückruffunktionen eine bestimmte MldNummer überwachen.

Jede dieser beiden Zeilen registriert eine Rückruffunktion, die nach allen zuvor registrierten Rückruffunktionen aufgerufen wird:

OnMessage MldNummer, Rückruf     ; Option 1 - MaxThreads weglassen
OnMessage MldNummer, Rückruf, 1  ; Option 2 - MaxThreads auf 1 setzen

Dies registriert eine Rückruffunktion, die vor allen zuvor registrierten Rückruffunktionen aufgerufen wird:

OnMessage MldNummer, Rückruf, -1

Um eine Rückruffunktion zu deregistrieren, geben Sie 0 für MaxThreads an:

OnMessage MldNummer, Rückruf, 0

Zusätzliche Infos für die Rückruffunktion

Neben den oben genannten Parametern kann die Rückruffunktion auch auf die interne Variable A_EventInfo zugreifen, die 0 enthält, wenn die Meldung via SendMessage gesendet wurde. Wenn die Meldung via PostMessage gesendet wurde, enthält die Variable den Tick-Count zum Zeitpunkt, als die Meldung gepostet wurde.

Das zuletzt gefundene Fenster einer Rückruffunktion ist vorerst das übergeordnete Fenster, an das die Meldung gesendet wurde (selbst wenn sie an ein Steuerelement gesendet wurde). Wenn das Fenster versteckt und kein GUI-Fenster ist (wie z.B. das Hauptfenster des Skripts), schalten Sie DetectHiddenWindows ein, bevor Sie es verwenden. Zum Beispiel:

DetectHiddenWindows True
MldElternfenster := WinExist()  ; Speichert die eindeutige ID des Fensters, das die Meldung empfangen hat.

Was die Rückruffunktion zurückgeben muss

Wenn eine Rückruffunktion Return verwendet, dessen Parameter leer ist oder weggelassen wird, oder überhaupt kein Return verwendet, wird die eingehende Meldung nach Abschluss der Rückruffunktion normal weiterverarbeitet. Das gleiche geschieht, wenn die Rückruffunktion mit Exit beendet wird oder einen Laufzeitfehler verursacht, z.B. durch Ausführen einer nicht-existierenden Datei. Die Rückgabe eines Integers bewirkt hingegen ein sofortiges Senden der Antwort, d.h. das Programm verarbeitet die Meldung nicht weiter. Zum Beispiel kann eine Rückruffunktion, die WM_LBUTTONDOWN (0x0201) überwacht, einen Integer zurückgeben, um das Zielfenster daran zu hindern, eine Benachrichtigung zu erhalten, dass ein Mausklick stattgefunden hat. In vielen Fällen (z.B. eine Meldung, die via PostMessage eintrifft) spielt es keine Rolle, welcher Integer zurückgegeben wird. Im Zweifelsfall ist 0 die sicherste Wahl.

Der Bereich von gültigen Rückgabewerten hängt davon ab, ob die EXE-Datei, die das Skript ausführt, 32-Bit oder 64-Bit ist. Bei einem 32-Bit-Skript (A_PtrSize = 4) müssen die Rückgabewerte im Bereich von -231 und 232-1 liegen, und bei einem 64-Bit-Skript (A_PtrSize = 8) im Bereich von -263 und 263-1.

Wenn mehrere Rückruffunktionen eine bestimmte Meldungsnummer überwachen, werden sie nacheinander aufgerufen, bis eine von ihnen einen nicht-leeren Wert zurückgibt.

Allgemeine Bemerkungen

Im Gegensatz zu einem normalen Funktionsaufruf wird bei Ankunft einer überwachten Meldung die Rückruffunktion als neuer Thread aufgerufen. Aus diesem Grund verwendet die Rückruffunktion vorerst die Standardwerte von Einstellungen wie SendMode und DetectHiddenWindows. Diese Standardwerte können während der Startphase des Skripts geändert werden.

Meldungen, die an ein Steuerelement gesendet (nicht gepostet) wurden, werden nicht überwacht, da das System diese direkt an das Steuerelement weiterleitet. Dies ist für systemgenerierte Meldungen nur selten ein Problem, weil die meisten von ihnen gepostet werden.

Wenn das Skript im Leerlauf weiterlaufen soll, um auf eingehende Meldungen zu warten, kann es erforderlich sein, die Persistent-Funktion aufzurufen, um die Beendigung des Skripts zu verhindern. OnMessage macht das Skript nicht automatisch persistent, da dies manchmal unnötig oder unerwünscht ist. Wenn OnMessage z.B. verwendet wird, um Eingaben in einem GUI-Fenster zu überwachen (wie im WM_LBUTTONDOWN-Beispiel), ist es oft sinnvoller, das Skript automatisch beenden zu lassen, wenn das letzte GUI-Fenster geschlossen wird.

Wenn eine Meldung eintrifft, während die Rückruffunktion aufgrund einer früheren Ankunft derselben Meldung noch läuft, wird die Rückruffunktion standardmäßig nicht erneut aufgerufen; stattdessen wird die Meldung als unüberwacht behandelt. Falls das unerwünscht ist, gibt es mehrere Möglichkeiten, dies zu vermeiden:

Wenn eine überwachte Meldung größer als 0x0311 gepostet wird, während das Skript unterbrechungsfrei ist, wird die Meldung gepuffert, d.h. ihre Rückruffunktion wird erst aufgerufen, wenn das Skript wieder unterbrechbar wird. Allerdings können Meldungen, die gesendet statt gepostet werden, nicht gepuffert werden, da sie einen Rückgabewert liefern müssen. Gepostete Meldungen werden möglicherweise auch nicht gepuffert, wenn eine modale Meldungsschleife läuft, z.B. für ein Systemdialogfenster, eine ListView-Ziehen-Ablegen-Operation oder ein Menü.

Wenn eine überwachte Meldung eintrifft und nicht gepuffert wird, wird ihre Rückruffunktion sofort aufgerufen, auch dann, wenn der Thread bei Ankunft der Meldung unterbrechungsfrei ist.

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

Bei der Überwachung von Systemmeldungen (unter 0x0400) ist Vorsicht geboten. Wenn z.B. eine Rückruffunktion nicht schnell zum Ende kommt, kann die Antwort auf die Meldung länger dauern als vom System erwartet, was zu Nebeneffekten führen kann. Unerwünschtes Verhalten kann auch auftreten, wenn eine Rückruffunktion die Weiterverarbeitung einer Meldung durch Rückgabe eines Integers unterdrückt, während das System eine andere Verarbeitung oder Antwort erwartet.

Wenn das Skript ein Systemdialogfenster wie z.B. MsgBox anzeigt, werden Meldungen, die an ein Steuerelement gepostet werden, nicht überwacht. Wenn z.B. das Skript ein Mitteilungsfenster anzeigt und der Benutzer eine Schaltfläche in einem GUI-Fenster anklickt, wird die WM_LBUTTONDOWN-Meldung direkt an die Schaltfläche gesendet, ohne die Rückruffunktion aufzurufen.

Ein externes Programm kann Meldungen mittels PostThreadMessage() oder einer anderen API-Funktion direkt an einen Thread des Skripts senden, aber dies wird nicht empfohlen, da solche Meldungen verloren gehen, wenn das Skript ein Systemdialogfenster wie z.B. MsgBox anzeigt. Stattdessen ist es besser, die Meldungen an das Hauptfenster des Skripts oder an eines seiner GUI-Fenster zu posten oder zu senden.

CallbackCreate, OnExit, OnClipboardChange, PostMessage, SendMessage, Funktionen, Windows-Meldungen, Threads, Critical, DllCall

Beispiele

Überwacht Mausklicks in einem GUI-Fenster. Siehe auch: ContextMenu-Ereignis

MeineGui := Gui(, "Beispielfenster")
MeineGui.Add("Text",, "Klicken Sie irgendwo auf das Fenster.")
MeineGui.Add("Edit", "w200")
MeineGui.Show
OnMessage 0x0201, WM_LBUTTONDOWN

WM_LBUTTONDOWN(wParam, lParam, msg, hwnd)
{
    X := lParam & 0xFFFF
    Y := lParam >> 16
    Strlmnt := ""
    thisGui := GuiFromHwnd(hwnd)
    thisGuiControl := GuiCtrlFromHwnd(hwnd)
    if thisGuiControl
    {
        thisGui := thisGuiControl.Gui
        Strlmnt := "`n(im Steuerelement " . thisGuiControl.ClassNN . ")"
    }
    ToolTip "Sie haben im GUI-Fenster '" thisGui.Title "' auf die Koordinaten " X "x" Y " geklickt." Strlmnt
}

Erkennt das Herunterfahren/Abmelden des Systems und erlaubt dem Benutzer, diesen Vorgang abzubrechen. In Windows Vista und höher erscheint eine Benutzeroberfläche, die anzeigt, welches Programm das Herunterfahren/Abmelden blockiert, und dem Benutzer ermöglicht, das Herunterfahren/Abmelden zu erzwingen. In älteren Betriebssystemen zeigt das Skript eine Sicherheitsabfrage an. Siehe auch: OnExit

; Das folgende DllCall ist optional: Es teilt dem OS mit, dieses Skript zuerst zu beenden (vor allen anderen Anwendungen).
DllCall("kernel32.dll\SetProcessShutdownParameters", "UInt", 0x4FF, "UInt", 0)
OnMessage(0x0011, On_WM_QUERYENDSESSION)
Persistent

On_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"
    try
    {
        ; Bestimmt einen Anzeigetext für die OS-Herunterfahren-UI. Wir
        ; zeigen keine eigene Sicherheitsabfrage an, da wir nur 5 Sekunden
        ; haben, bevor das OS von sich aus die Herunterfahren-UI anzeigt.  
        ; Außerdem kann ein Programm ohne sichtbares Fenster das
        ; Herunterfahren nur blockieren, wenn ein Grund angegeben ist.
        HerunterfahrenSperren("Es wird versucht, " Ereignis " zu verhindern.")
        return false
    }
    catch
    {
        ; ShutdownBlockReasonCreate ist nicht verfügbar, demzufolge läuft
        ; vermutlich Windows XP, 2003 oder 2000, wo wir tatsächlich
        ; das Herunterfahren verhindern können.
        Ergebnis := MsgBox(Ereignis " aktiv. Erlauben?",, "YN")
        if (Ergebnis = "Yes")
            return true  ; Erlaubt dem OS das Herunterfahren/Abmelden.
        else
            return false  ; Verbietet dem OS das Herunterfahren/Abmelden.
    }
}

HerunterfahrenSperren(Grund)
{
    ; Wenn Ihr Skript eine sichtbare GUI hat, nutzen Sie diese anstelle
    ; von A_ScriptHwnd.
    DllCall("ShutdownBlockReasonCreate", "ptr", A_ScriptHwnd, "wstr", Grund)
    OnExit HerunterfahrenEntsperren
}

HerunterfahrenEntsperren(*)
{
    OnExit HerunterfahrenEntsperren, 0
    DllCall("ShutdownBlockReasonDestroy", "ptr", A_ScriptHwnd)
}

Empfängt eine benutzerdefinierte Meldung und bis zu zwei Zahlen von einem anderen Skript oder Programm (um Zeichenketten statt Zahlen zu senden, siehe nächstes Beispiel).

OnMessage 0x5555, MldÜberwachung
Persistent

MldÜberwachung(wParam, lParam, Mld, *)
{
    ; Damit der Rückruf schnell zum Ende kommen kann, besser ToolTip verwenden,
    ; weil so etwas wie MsgBox dies verhindern würde:
    ToolTip "Meldung " Mld " empfangen:`nWPARAM: " wParam "`nLPARAM: " lParam
}

; Mit den folgenden Zeilen in einem anderen Skript kann der Rückruf im oberen Skript ausgeführt werden:
SetTitleMatchMode 2
DetectHiddenWindows True
if WinExist("Empfänger.ahk ahk_class AutoHotkey")
    PostMessage 0x5555, 11, 22  ; Die Meldung wird an das "zuletzt gefundene Fenster" gesendet, wegen WinExist oben.
DetectHiddenWindows False  ; Erst nach PostMessage ausschalten.

Sendet eine Zeichenkette beliebiger Länge an ein anderes Skript. Speichern und starten Sie die beiden folgenden Skripte und drücken Sie Win+Leer, um ein Eingabefenster anzuzeigen, das den Benutzer zur Eingabe einer Zeichenkette auffordert. Beide Skripte müssen dieselbe native Kodierung verwenden.

Speichern Sie das folgende Skript unter Receiver.ahk und starten Sie es.

#SingleInstance
OnMessage 0x004A, WM_COPYDATA_Empfangen  ; 0x004A ist WM_COPYDATA
Persistent

WM_COPYDATA_Empfangen(wParam, lParam, Mld, Hwnd)
{
    ZketteAdresse := NumGet(lParam, 2*A_PtrSize, "Ptr")  ; CopyDataStruct's lpData-Element abrufen.
    KopieDaten := StrGet(ZketteAdresse)  ; Zeichenkette aus der Struktur kopieren.
    ; ToolTip statt MsgBox für eine rechtzeitige Rückgabe verwenden:
    ToolTip A_ScriptName "`nhat die folgende Zeichenkette empfangen:`n" KopieDaten
    return true  ; Die Rückgabe von 1 (true) ist der übliche Weg zur Bestätigung dieser Meldung.
}

Speichern Sie das folgende Skript unter Sender.ahk und starten Sie es. Drücken Sie danach den Hotkey Win+Leer.

ZielSkriptTitel := "Receiver.ahk ahk_class AutoHotkey"

#space::  ; WIN+LEER-Hotkey. Drücken Sie ihn, um ein Fenster zur Eingabe des zu sendenden Texts anzuzeigen.
{
    ib := InputBox("Tragen Sie den zu sendenden Text ein:", "Text via WM_COPYDATA senden")
    if ib.Result = "Cancel"  ; Benutzer hat die Abbrechen-Schaltfläche gedrückt.
        return
    Ergebnis := WM_COPYDATA_Senden(ib.Value, ZielSkriptTitel)
    if Ergebnis = ""
        MsgBox "SendMessage fehlgeschlagen oder Zeit überschritten. Existiert der folgende 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."
}

WM_COPYDATA_Senden(ZuSendendeZkette, ZielSkriptTitel)
; Diese Funktion sendet eine bestimmte Zeichenkette an ein bestimmtes Fenster und gibt die Antwort zurück.
; Die Antwort ist 1, wenn das Zielfenster die Meldung verarbeitet hat, oder 0, wenn es diese ignoriert hat.
{
    KopieDatenStrukt := Buffer(3*A_PtrSize)  ; Speicherbereich der Struktur einrichten.
    ; Zuerst das cbData-Element der Struktur auf die Größe der Zeichenkette setzen, inklusive Nullterminator:
    GrößeInBytes := (StrLen(ZuSendendeZkette) + 1) * 2
    NumPut( "Ptr", GrößeInBytes  ; Muss für das OS getan werden.
          , "Ptr", StrPtr(ZuSendendeZkette)  ; lpData auf die Zeichenkette selbst verweisen lassen.
          , KopieDatenStrukt, A_PtrSize)
    Vorher_DetectHiddenWindows := A_DetectHiddenWindows
    Vorher_TitleMatchMode := A_TitleMatchMode
    DetectHiddenWindows True
    SetTitleMatchMode 2
    TimeoutWert := 4000  ; Optional. Zeit in Millisekunden, die Empfänger.ahk zum Antworten hat. Standard ist 5000
    ; Send muss verwendet werden, nicht Post.
    RückWert := SendMessage(0x004A, 0, KopieDatenStrukt,, ZielSkriptTitel,,,, TimeoutWert) ; 0x004A ist WM_COPYDATA.
    DetectHiddenWindows Vorher_DetectHiddenWindows  ; Ursprüngliche Einstellung für den Aufrufer wiederherstellen.
    SetTitleMatchMode Vorher_TitleMatchMode         ; Hier auch.
    return RückWert  ; Die Antwort von SendMessage an den Aufrufer zurückgeben.
}

Das WinLIRC-Client-Skript zeigt Ihnen, wie mit OnMessage eine Benachrichtigung empfangen werden kann, wenn Daten über eine Netzwerkverbindung eingegangen sind.