OnMessage()

Bestimmt eine Funktion oder ein Funktionsobjekt, das automatisch aufgerufen werden soll, wenn das Skript eine bestimmte Meldung empfängt.

OnMessage(MldNummer , Funktion, MaxThreads)

Parameter

MldNummer

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 0x0400), 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 in [v1.1.20+] ein Funktionsobjekt. Um einen direkt geschriebenen 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 finden Sie unter Funktionsname vs. Objekt.

MaxThreads [v1.0.47+]

Normalerweise lässt man diesen Parameter 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.

Wenn die überwachende Funktion direkt oder indirekt ein erneutes Senden der Meldung bewirkt, während die Funktion noch läuft, muss ein MaxThreads-Wert größer als 1 oder kleiner als -1 angegeben werden, damit die überwachende Funktion 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.

[v1.1.20+]: Geben Sie 0 an, um eine zuvor registrierte Funktion zu deregistrieren. Wenn Funktion eine Zeichenkette ist, wird die "altmodische" Überwachung entfernt. Andernfalls wird nur das angegebene Funktionsobjekt deregistriert.

[v1.1.20+]: Standardmäßig werden mehrere Funktionen, die für eine einzige MldNummer registriert sind, in der Reihenfolge aufgerufen, wie sie registriert wurden. Um die zu registrierende Funktion vor allen anderen bereits registrierten Funktionen aufzurufen, geben Sie für MaxThreads einen negativen Wert an. Zum Beispiel würde OnMessage(Mld, Fn, -2) die Funktion Fn vor allen anderen registrierten Funktionen für Mld 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 MldNummer 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 MldNummer (lassen Sie die Anführungszeichen weg, wenn Sie eine Variable übergeben wollen):

Name := OnMessage(MldNummer, "FunktionName")

Der Rückgabewert ist einer der folgenden:

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

Name := OnMessage(MldNummer, "")

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

Name := OnMessage(MldNummer)

Funktionsobjekt

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

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

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

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

OnMessage(MldNummer, FunkObj, -1)

Um die Registrierung eines Funktionsobjekts rückgängig zu machen, geben Sie für MaxThreads eine 0 an:

OnMessage(MldNummer, 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 zum Überwachen von einer oder mehreren Meldungen zugeteilt wurde, akzeptiert bis zu vier Parameter:

MeineMldÜberwachung(wParam, lParam, Mld, Hwnd)
{
    ... Funktionskörper ...
}

Obwohl die Namen, die Sie den Parametern geben, keine Rolle spielen, werden ihnen die folgenden Informationen nacheinander zugewiesen:

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 zum Ausführen verwendete EXE-Datei 32- oder 64-Bit ist. Wenn bei einem 32-Bit-Skript ein eingehender Parameter als vorzeichenfähiger 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

Sie können eine oder mehrere Parameter vom Ende der Liste weglassen, wenn Sie die entsprechende Information nicht benötigen. Zum Beispiel würde MeineMldÜberwachung(wParam, lParam) nur die ersten zwei Parameter empfangen und MeineMldÜberwachung() gar keine.

Weitere verfügbare Informationen für die Funktion

Neben den oben genannten Parametern kann die Funktion auch auf die Werte der folgenden internen 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
MldÜbergeordFenster := WinExist()  ; Dies speichert die eindeutige ID des Fensters, das die Meldung erhalten hat.

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). Verwendet 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 (0x0201) ü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 verwendet die Funktion zu Beginn die Standardwerte von Einstellungen wie SendMode und DetectHiddenWindows. Diese Standardwerte 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 wird bei solchen Skripten standardmäßig ein Dialogfenster mit der Anfrage angezeigt, ob die alte Instanz beibehalten oder mit einer neuen Instanz ersetzt werden soll, es sei denn, man überschreibt das mit der #SingleInstance-Direktive.

Wenn eine Meldung eintrifft, während die Funktion noch läuft, wird die Funktion standardmäßig nicht erneut aufgerufen; stattdessen wird die Meldung so behandelt, als würde sie nicht überwacht werden. Wenn das unerwünscht ist, gibt es mehrere Möglichkeiten, um das zu vermeiden:

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

Wenn eine überwachte Meldung eintrifft, wird, wenn sie nicht gepuffert ist und das Skript aufgrund von Thread Interrupt oder Critical unterbrechungsfrei ist, der aktuelle Thread unterbrochen, so dass die Funktion aufgerufen werden kann. Wenn das Skript aber 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 würde sie nicht überwacht werden.

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

Seien Sie vorsichtig, wenn Sie Systemmeldungen (solche unter 0x0400) überwachen. 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 an ein Steuerelement gesendet werden, nicht überwacht. Wenn beispielsweise das Skript ein Mitteilungsfenster anzeigt und der Benutzer innerhalb eines GUI-Fensters auf eine Schaltfläche klickt, wird die WM_LBUTTONDOWN-Meldung direkt an die Schaltfläche gesendet, ohne dass die überwachende Funktion aufgerufen wird.

Ein externes Programm kann Meldungen mithilfe von PostThreadMessage() oder einer anderen API-Funktion direkt an ein Thread des Skripts senden, allerdings wird dies nicht empfohlen, weil solche Meldungen verloren gehen würden, wenn das Skript ein Dialogfenster wie MsgBox anzeigt. Stattdessen ist es besser, Meldungen an das Hauptfenster des Skripts oder an eines seiner GUI-Fenster zu senden.

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

Beispiele

Überwacht Mausklicks innerhalb eines GUI-Fensters. Verwandtes Thema: GuiContextMenu

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

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

GuiClose:
ExitApp

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. Verwandtes Thema: OnExit

; Das folgende DllCall ist optional: es 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(0x0011, "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"
    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.
        BlockiereHerunterfahren("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.
        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.
    }
}

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

StoppeBlockenHerunterfahren()
{
    OnExit(A_ThisFunc, 0)
    DllCall("ShutdownBlockReasonDestroy", "ptr", A_ScriptHwnd)
}

Empfängt eine benutzerdefinierte Meldung und bis zu zwei Zahlen von einem anderen Skript oder Programm (schauen Sie sich das Beispiel danach an, wie man Zeichenketten sendet).

OnMessage(0x5555, "MldÜberwachung")
OnMessage(0x5556, "MldÜberwachung")

MldÜberwachung(wParam, lParam, Mld)
{
    ; Damit die Funktion schnell zum Ende kommen kann, ist es besser, ToolTip zu verwenden,
    ; weil so etwas wie MsgBox dies verhindern würde:
    ToolTip Meldung %Mld% empfangen:`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.

Sendet eine Zeichenkette mit beliebiger Länge an ein anderes Skript. Um dies zu nutzen, speichern und starten Sie beide folgenden Skripte und drücken Sie Win+Leer, um ein Eingabefenster anzuzeigen, die den Benutzer zur Eingabe einer Zeichenkette auffordert. Beide Skripte müssen die gleiche native Codierung verwenden.

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

#SingleInstance
OnMessage(0x004A, "Empfange_WM_COPYDATA")  ; 0x004A ist WM_COPYDATA
return

Empfange_WM_COPYDATA(wParam, lParam)
{
    ZketteAdresse := NumGet(lParam + 2*A_PtrSize)  ; Ermittelt die Adresse des lpData-Elements in CopyDataStruct.
    KopieDaten := StrGet(ZketteAdresse)  ; 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%KopieDaten%
    return true  ; Die Rückgabe einer 1 (wahr) ist der übliche Weg, um diese Meldung zu bestätigen.
}

Speichern Sie das folgende Skript als 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 diesen, um ein Eingabefenster zur Eingabe der zu sendenden Zeichenkette anzuzeigen.
InputBox, ZuSendendeZkette, Text via WM_COPYDATA senden, Tragen Sie den zu sendenden Text ein:
if ErrorLevel  ; Benutzer hat die Abbrechen-Schaltfläche gedrückt.
    return
Ergebnis := Sende_WM_COPYDATA(ZuSendendeZkette, 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 ZuSendendeZkette, ByRef ZielSkriptTitel)  ; ByRef verbraucht in diesem Fall weniger Speicher.
; Diese Funktion sendet eine bestimmte Zeichenkette an ein bestimmtes 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(ZuSendendeZkette) + 1) * (A_IsUnicode ? 2 : 1)
    NumPut(GrößeInBytes, KopieDatenStrukt, A_PtrSize)  ; Muss für das OS getan werden.
    NumPut(&ZuSendendeZkette, 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, 0x004A, 0, &KopieDatenStrukt,, %ZielSkriptTitel%,,,, %TimeoutWert% ; 0x004A 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.
}

Im WinLIRC-Client-Skript wird gezeigt, wie man OnMessage() nutzen kann, um bei Eintreffen von Daten über die Netzwerkverbindung eine Benachrichtigung zu erhalten.