OnMessage()

스크립트가 지정된 메시지를 받을 때 자동으로 호출할 함수 또는 함수 객체를 지정합니다 .

OnMessage(MsgNumber , Function, MaxThreads)

매개변수

MsgNumber

감시 또는 질의할 메시지 번호, 범위는 0부터 4294967295 (0xFFFFFFFF)까지. 시스템 메시지 (즉, 0x0400 아래의 메시지)를 감시하고 싶지 않으면, 가능하면 4096 (0x1000)보다 더 큰 번호를 고르는 것이 제일 좋습니다. 이렇게 하면 오토핫키에 내부적으로 사용되는 메시지를 간섭할 기회가 줄어듭니다.

Function

함수의 이름, [v1.1.20+]에서는 함수 객체. 기호 그대로의 함수 이름을 건네려면, 따옴표로 둘러 싸야 합니다.

어떻게 함수가 등록되는가 그리고 OnMessage의 반환 값은 이 매개변수가 문자열인가 아니면 함수 객체인가에 달려 있습니다. 더 자세한 것은 함수 이름 vs 객체를 참조하십시오.

MaxThreads [v1.0.47+]

이 정수는 보통 생략됩니다. 이 경우 감시 함수는 한 번에 한 쓰레드로 제한됩니다. 이것이 보통 제일 좋습니다. 그렇지 않으면 스크립트가 감시 함수가 자신을 호출하면 시간 순서가 엉망인 채로 메시지들을 처리하기 때문입니다. 그러므로, MaxThreads에 대한 대안으로서, 아래에 기술하는 것처럼 Critical을 사용을 고려해 보십시오.

If the monitor function directly or indirectly causes the message to be sent again while the function is still running, it is necessary to specify a MaxThreads value greater than 1 or less than -1 to allow the monitor function to be called for the new message (if desired). Messages sent (not posted) by the script's own process to itself cannot be delayed or buffered.

[v1.1.20+]: Specify 0 to unregister a previously registered function. If Function is a string, the "legacy" monitor is removed. Otherwise, only the given function object is unregistered.

[v1.1.20+]: 기본값으로, 여러 함수가 하나의 MsgNumber에 등록되어 있으면, 등록된 순서대로 호출됩니다. 이전에 등록된 함수보다 먼저 호출될 함수를 등록하려면, MaxThreads에 음수를 지정하십시오. 예를 들어, OnMessage(Msg, Fn, -2)는 이전에 Msg에 등록된 다른 어떤 함수보다도 먼저 Fn을 호출되도록 등록합니다. 그리고 Fn에 최대 2 개의 쓰레드를 허용합니다. 그렇지만, 그 함수가 이미 등록되어 있으면, 등록을 해제하고 다시 재등록 하지 않은 한 순서가 바뀌지 않습니다.

함수 이름 vs 함수

OnMessage의 반환 값과 행위는 Function 매개변수가 함수 이름인가 아니면 객체인가에 따라 다릅니다.

함수 이름

하위 호환을 위해, 각각의 유일한 MsgNumber를 감시하려면 기껏해야 한 개의 함수만 이름으로 등록할 수 있습니다 -- 이것을 "legacy" 감시라고 부릅니다.

legacy 감시 함수가 처음 등록되면, 이전에 등록된 감시 함수들 앞에 호출될지 뒤에 호출될지는 MaxThreads 매개변수에 달려 있습니다. 이 감시 함수가 다른 함수를 호출하도록 업데이트하더라도 먼저 등록해제 하지 않는 한 순서에 영향을 주지 않습니다.

다음 코드는 MsgNumber에 대하여 현재 legacy 감시 함수를 등록하거나 업데이트합니다 (변수를 건넨다면 따옴표는 생략합니다):

Name := OnMessage(MsgNumber, "FunctionName")

반환 값은 다음 중 하나입니다:

다음은 MsgNumber에 대하여 현재의 legacy 감시자를 (있다면) 등록을 해제하고 그의 이름을 돌려줍니다 (없으면 빈 값을 돌려줌):

Name := OnMessage(MsgNumber, "")

다음은 MsgNumber에 대하여 현재의 legacy 감시자의 이름을 돌려줍니다 (없다면 빈 값을 돌려줌):

Name := OnMessage(MsgNumber)

함수 객체

(정상 함수를 포함하여) 갯수에 상관없이 얼마든지 함수 객체는 주어진 MsgNumber를 감시할 수 있습니다.

다음 두 줄은 이전에 등록한 함수들 다음에 호출될 함수 객체를 등록합니다:

OnMessage(MsgNumber, FuncObj)     ; Option 1
OnMessage(MsgNumber, FuncObj, 1)  ; Option 2 (MaxThreads = 1)

다음은 이전에 등록된 함수보다 먼저 호출될 함수 객체를 등록합니다:

OnMessage(MsgNumber, FuncObj, -1)

함수 객체를 등록 해제하려면 MaxThreads에 0을 지정하십시오:

OnMessage(MsgNumber, FuncObj, 0)

실패

실패는 Function이 다음과 같은 경우에 일어납니다:

  1. 객체가 아니거나, 사용자-정의 함수의 이름 또는 빈문자열입니다;
  2. 매개변수를 다섯 개 이상 요구한다고 알려져 있습니다
  3. 또는 [v1.0.48.05] 이전에서, ByRef 또는 선택적 매개변수를 가지고 있습니다.

[v1.1.19.03] 이전에서 실패는 이미 500 개의 메시지를 감시 중인데 스크립트가 새 메시지를 감시하려고 시도하는 경우에도 일어납니다.

Function이 객체이면, 실패시 예외를 던집니다. 그렇지 않으면, 빈 문자열을 돌려줍니다.

함수의 매개변수

여러 메시지를 감시하도록 할당된 함수는 최대 네 개까지 매개변수를 받을 수 있습니다:

MyMessageMonitor(wParam, lParam, msg, hwnd)
{
    ... body of function...
}

매개변수에 건넨 이름에 상관 없이, 다음 정보가 순서대로 매개변수에 할당됩니다.:

WPARAM 그리고 LPARAM은 스크립트의 실행 파일이 32-비트인지 64-비트인지에 따라 부호 없는 32-비트 정수이거나 (0부터 232-1까지) 또는 부호 있는 64-비트 정수입니다 (-263부터 263-1까지). 32-비트 스크립트에 대하여, 들어오는 매개변수가 부호 있는 정수를 의도한다면, 음수는 다음 예제를 따라 노출시킬 수 있습니다:

if (A_PtrSize = 4 && wParam > 0x7FFFFFFF)  ; A_PtrSize를 점검해 스크립트가 32-비트임을 확인합니다.
    wParam := -(~wParam) - 1

상응하는 정보가 필요하지 않으면 리스트의 끝으로부터 하나 이상의 매개변수를 생략할 수 있습니다. 예를 들어, MyMsgMonitor(wParam, lParam)로 정의된 함수는 앞에서 두 개의 매개변수만 받습니다. 그리고 MyMsgMonitor()로 정의된 함수는 매개변수를 전혀 받지 않습니다.

함수에서 사용가능한 추가 정보

위에서 받은 매개변수 말고도, 함수는 다음 내장 변수로부터 값들을 참고할 수도 있습니다:

감시 함수의 마지막 발견 창은 메시지가 전송된 부모 창으로 시작합니다 (콘트롤에 전송되었어도 마찬가지입니다). 창은 숨어 있지만 GUI 창은 아니라면 (예를 들어 스크립트의 메인 창), 사용하기 전에 먼저 DetectHiddenWindows를 켜십시오. 예를 들어:

DetectHiddenWindows On
MsgParentWindow := WinExist()  ; 메시지가 전송된 창의 유일한 ID를 저장합니다.

함수는 무엇을돌려주어야 하는가

감시 함수가 매개변수 없이 Return을 사용하거나, ""와 같은 빈 값을 지정하면 (또는 전혀 Return을 사용하지 않으면), 들어오는 메시지는 함수가 끝날 때 정상적으로 처리됩니다. 함수가 종료하거나(Exits) 존재하지 않는 파일을 실행하는 실행시간 에러를 야기하더라도 같은 일이 일어납니다. 대조적으로, 정수를 돌려주면, 그 메시지는 즉시 반송됩니다; 즉, 프로그램은 메시지를 더 이상 처리하지 않습니다. 예를 들어, WM_LBUTTONDOWN (0x0201)을 감시하는 함수가 정수를 돌려주면 목표 창은 마우스 클릭이 일어난 사실을 고지 받지 못합니다. 많은 경우 (예를 들어 PostMessage를 통하여 도착하는 메시지의 경우), 어떤 정수가 반환되어도 상관하지 않습니다; 그러나 잘 모르겠다면, 0이 보통 가장 안전합니다.

유효한 반환 값의 범위는 스크립트의 실행파일이 32-비트인가 64-비트인가에 따라 다릅니다. 비어 있지 않은 값은 반드시 범위가 32비트-스크립트에 대하여 (A_PtrSize = 4) -231부터 232-1까지 그리고 64-비트 스크립트에 대하여(A_PtrSize = 8) -263부터 263-1까지입니다.

[v1.1.20+]: 하나의 메시지에 대하여 여러 감시 함수가 있다면, 비어 있지 않은 값을 돌려줄 때까지 하나씩 호출됩니다.

총평

정상 함수-호출과 다르게, 감시 메시지가 도착하면 함수는 새로운 쓰레드로 호출됩니다. 이 때문에, 함수는 SendMode 그리고 DetectHiddenWindows과 같은 기본값을 가지고 새롭게 시작합니다. 이 기본값은 자동-실행 섹션에서 바꿀 수 있습니다.

콘트롤에 (포스트 방식이 아니라) 센드 방식으로 전송되는 메시지는 관제되지 않습니다. 왜냐하면 시스템은 그런 메시지들을 화면 뒤의 콘트롤에 직접 보내 버리기 때문입니다. 이런 일은 시스템에서-생성되는 메시지들에는 별로 문제가 되지 않습니다. 왜냐하면 대부분 포스트 방식으로 전송되기 때문입니다..

OnMessage를 호출하는 스크립트는 자동으로 영속적이 됩니다. 또한 단일 실체이기도 합니다. 단, #SingleInstance를 사용하여 오버라이드된 경우는 예외입니다.

If a message arrives while its function is still running due to a previous arrival of the same message, by default the function will not be called again; instead, the message will be treated as unmonitored. If this is undesirable, there are multiple ways it can be avoided:

If a monitored message that is numerically greater than 0x0311 is posted while the script is uninterruptible, the message is buffered; that is, its function is not called until the script becomes interruptible. However, messages which are sent rather than posted cannot be buffered as they must provide a return value. Posted messages also might not be buffered when a modal message loop is running, such as for a system dialog, ListView drag-drop operation or menu.

When a monitored message arrives, if it is not buffered and the script is uninterruptible merely due to the settings of Thread Interrupt or Critical, the current thread will be interrupted so that the function can be called. However, if the script is absolutely uninterruptible -- such as while a menu is displayed, a KeyDelay/MouseDelay is in progress, or the clipboard is being opened -- the message's function will not be called and the message will be treated as unmonitored.

OnMessage 쓰레드의 우선 순위는 언제나 0입니다. 결과적으로 현재 쓰레드의 우선 순위가 0보다 높으면 어떤 메시지도 관제되지 않고 버퍼 처리되지 않습니다.

(0x0400미만의) 시스템 메시지는 주의해서 사용해야 합니다. 예를 들어, 감시 함수가 빨리 일을 끝내지 못하면, 그 메시지에 대한 응답은 시스템의 예상보다 훨씬 더 걸립니다. 이 때문에 부작용을 야기할 수 있습니다. 감시 프로그램이 더 이상의 메시지 처리를 억누르기 위해 정수를 돌려주면 원하지 않는 행위가 일어날 수도 있습니다. 시스템은 다른 처리 다른 응답을 예상하고 있기 때문입니다.

스크립트가 MsgBox와 같은 시스템 창을 보여주고 있을 때, 콘트롤에 포스트되는 메시지는 모두 관제가 되지 않습니다. 예를 들어, 스크립트가 메시지 상자를 보여주고 있고 사용자가 GUI 창에 버튼을 클릭하면, WM_LBUTTONDOWN 메시지는 감시 함수를 호출하지 않고 직접 버튼에 전송됩니다.

외부 프로그램은 PostThreadMessage() 또는 기타 API 호출을 통하여 메시지를 직접 스크립트의 쓰레드에 포스트할 수 있지만, 이것은 권장하지 않습니다. 왜냐하면 그 메시지들은 스크립트가 메시지 상자 같은 시스템 창을 보여주고 있는 중이라면 소실되어 버리기 때문입니다. 대신에, 보통 스크립트의 메인 창이나 그의 GUI 창 중의 하나에 메시지를 보내는 것이 가장 좋습니다 (send vs post).

RegisterCallback(), OnExit(), OnExit, OnClipboardChange(), OnClipboardChange 라벨, Post/SendMessage, 함수, 창 메시지 목록, 쓰레드, Critical, DllCall()

예제

GUI 창에서 마우스 클릭을 감시합니다. 관련 주제: GuiContextMenu

Gui, Add, Text,, 이 창 아무데나 클릭하십시오.
Gui, Add, Edit, w200 vMyEdit
Gui, Show
OnMessage(0x0201, "WM_LBUTTONDOWN")
return

WM_LBUTTONDOWN(wParam, lParam)
{
    X := lParam & 0xFFFF
    Y := lParam >> 16
    if A_GuiControl
        Ctrl := "`n(in control " . A_GuiControl . ")"
    ToolTip Gui #%A_Gui% 창에서 클라이언트 좌표 %X%x%Y%.%Ctrl%에 왼클릭 했습니다.
}

GuiClose:
ExitApp

Detects system shutdown/logoff and allows the user to abort it. On Windows Vista and later, the system displays a user interface showing which program is blocking shutdown/logoff and allowing the user to force shutdown/logoff. On older OSes, the script displays a confirmation prompt. 관련 주제: OnExit

; 다음 DllCall은 선택적입니다: OS에게 이 스크립트를 (다른 모든 어플리케이션보다) 먼저 닫으라고 명령합니다.
DllCall("kernel32.dll\SetProcessShutdownParameters", "UInt", 0x4FF, "UInt", 0)
OnMessage(0x0011, "WM_QUERYENDSESSION")
return

WM_QUERYENDSESSION(wParam, lParam)
{
    ENDSESSION_LOGOFF := 0x80000000
    if (lParam & ENDSESSION_LOGOFF)  ; 사용자가 로그오프중입니다.
        EventType := "Logoff"
    else  ; 시스템은 셧다운 또는 재시작 중입니다.
        EventType := "Shutdown"
    try
    {
        ; Set a prompt for the OS shutdown UI to display.  We do not display
        ; our own confirmation prompt because we have only 5 seconds before
        ; the OS displays the shutdown UI anyway.  Also, a program without
        ; a visible window cannot block shutdown without providing a reason.
        BlockShutdown("Example script attempting to prevent " EventType ".")
        return false
    }
    catch
    {
        ; ShutdownBlockReasonCreate is not available, so this is probably
        ; Windows XP, 2003 or 2000, where we can actually prevent shutdown.
        MsgBox, 4,, %EventType% in progress.  Allow it?
        IfMsgBox Yes
            return true  ; OS에게 셧다운/로그오프를 계속 허용하도록 명령합니다.
        else
            return false  ; OS에게 셧다운/로그오프를 취소하도록 명령합니다.
    }
}

BlockShutdown(Reason)
{
    ; If your script has a visible GUI, use it instead of A_ScriptHwnd.
    DllCall("ShutdownBlockReasonCreate", "ptr", A_ScriptHwnd, "wstr", Reason)
    OnExit("StopBlockingShutdown")
}

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

스크 프로그램으로부터 맞춤 메시지와 최대 두 개의 숫자를 받도록 만듭니다 (숫자 대신에 문자열을 전송하려면, 이 다음 예제를 보십시오).

OnMessage(0x5555, "MsgMonitor")
OnMessage(0x5556, "MsgMonitor")

MsgMonitor(wParam, lParam, msg)
{
    ; 신속하게 돌아가는 것이 중요하다면, ToolTip을 사용하는 편이 더 좋습니다.
    ; MsgBox 같은 경우는 함수가 일을 끝내지 못하도록 방해하기 때문입니다:
    ToolTip Message %msg% arrived:`nWPARAM: %wParam%`nLPARAM: %lParam%
}

; 다음을 다른 스크립트 안에서 사용하면 위의 스크립트 안에서 함수를 실행할 수 있습니다:
SetTitleMatchMode 2
DetectHiddenWindows On
if WinExist("Name of Receiving Script.ahk ahk_class AutoHotkey")
    PostMessage, 0x5555, 11, 22  ; 이 메시지는 위의 WinExist() 때문에 "마지막 발견 창"으로 전송됩니다.
DetectHiddenWindows Off  ; PostMessage가 끝날 때까지 끄면 안됩니다.

문자열을 길이에 상관없이 한 스크립트에서 다른 스크립트로 전송합니다. To use this, save and run both of the following scripts then press Win+Space to show an input box that will prompt you to type in a string. Both scripts must use the same native encoding.

다음 스크립트를 Receiver.ahk로 저장하고 기동시키십시오.

#SingleInstance
OnMessage(0x004A, "Receive_WM_COPYDATA")  ; 0x004A은 WM_COPYDATA입니다
return

Receive_WM_COPYDATA(wParam, lParam)
{
    StringAddress := NumGet(lParam + 2*A_PtrSize)  ; CopyDataStruct의 lpData 멤버를 받습니다.
    CopyOfData := StrGet(StringAddress)  ; 구조체로부터 문자열을 복사합니다.
    ; 그것을 ToolTip vs. MsgBox로 보여줍니다. 그래서 적절한 시간에 돌려줄 수 있습니다:
    ToolTip %A_ScriptName%`nReceived the following string:`n%CopyOfData%
    return true  ;  1 (true)을 돌려주는 것이 이 메시지를 승인하는 전통적인 방식입니다.
}

다음 스크립트를 Sender.ahk로 저장한 다음 기동시키십시오. 그 다음, Win+Space 핫키를 누르십시오.

TargetScriptTitle := "Receiver.ahk ahk_class AutoHotkey"

#space::  ; Win+Space 핫키. 누르면 메시지 문자열을 입력할 입력 박스가 나타납니다.
InputBox, StringToSend, Send text via WM_COPYDATA, Enter some text to Send:
if ErrorLevel  ; 사용자가 취소 버튼을 눌렀습니다.
    return
result := Send_WM_COPYDATA(StringToSend, TargetScriptTitle)
if (result = "FAIL")
    MsgBox SendMessage failed. Does the following WinTitle exist?:`n%TargetScriptTitle%
else if (result = 0)
    MsgBox Message sent but the target window responded with 0, which may mean it ignored it.
return

Send_WM_COPYDATA(ByRef StringToSend, ByRef TargetScriptTitle)  ; ByRef는 이 경우 메모리를 상당히 절약해 줍니다.
; 다음 함수는 지정된 문자열을 지정된 창에 전송하고 응답을 돌려줍니다.
; 목표 창이 메시지를 처리했으면 응답은 1입니다. 또는 무시했다면 0입니다.
{
    VarSetCapacity(CopyDataStruct, 3*A_PtrSize, 0)  ; 구조체이 메모리 구역을 설정합니다.
    ; 먼저 구조체의 cbData 멤버에 문자열의 크기를 설정합니다. 그의 0 종료문자를 포함합니다:
    SizeInBytes := (StrLen(StringToSend) + 1) * (A_IsUnicode ? 2 : 1)
    NumPut(SizeInBytes, CopyDataStruct, A_PtrSize)  ; OS의 요구대로 이렇게 처리합니다.
    NumPut(&StringToSend, CopyDataStruct, 2*A_PtrSize)  ; lpData에 문자열 자체를 가리키도록 설정합니다.
    Prev_DetectHiddenWindows := A_DetectHiddenWindows
    Prev_TitleMatchMode := A_TitleMatchMode
    DetectHiddenWindows On
    SetTitleMatchMode 2
    TimeOutTime := 4000  ; Optional. Milliseconds to wait for response from receiver.ahk. 기본 값은 5000입니다
    ; Must use SendMessage not PostMessage.
    SendMessage, 0x004A, 0, &CopyDataStruct,, %TargetScriptTitle%,,,, %TimeOutTime% ; 0x004A은 WM_COPYDATA입니다.
    DetectHiddenWindows %Prev_DetectHiddenWindows%  ; 호출자에 대하여 원래 설정을 복구합니다.
    SetTitleMatchMode %Prev_TitleMatchMode%         ; 위와 같습니다.
    return ErrorLevel  ; SendMessage의 응답을 다시 호출자에게 전송합니다.
}

See the WinLIRC client script for a demonstration of how to use OnMessage() to receive notification when data has arrived on a network connection.