RegisterCallback()

提供: AutoHotkey Wiki
移動: 案内検索

実行制御 | GUI表示 | 演算・変数 | メモリ・DLL操作 | 文字列操作 | キーボード | マウス | シェル | ウィンドウ | ウィンドウグループ
ステータスバー | コントロール | サウンド | ファイル | INIファイル | レジストリ | 環境変数 | AutoHotkey | その他 | 設定関係 | オブジェクト

RegisterCallback() [v1.0.47+][編集]

DllCall()で呼び出した関数などからスクリプト内の関数を呼び出すためのラッパー関数を生成し、アドレスを返す。

RegisterCallback("FunctionName" [, Options = "", ParamCount = FormalCount, EventInfo = Address])

Parameters[編集]

引数名 説明
FunctionName スクリプト内の関数名を文字列値として指定する。
Options 以下のオプションを半角スペース区切りで指定する。例) "C Fast"
詳しくは FastモードとSlowモードを参照のこと。
Fast または F FunctionName で指定した関数を実行するとき、新たなスレッドを開始しないことにより、処理速度を向上させる。
CDecl または C 呼び出し規約が CDecl方式の関数を生成したい場合に指定する。
ParamCount 関数が受け取る引数の数を指定する。
省略時は、 FunctionName の関数に定義されている必須の引数と同じ数になる。
FunctionName に指定されている引数の数より多かったり、必須の引数の数より少なかったりしてはならない。
EventInfo 作成されたコールバック関数からスクリプトの関数が呼ばれるときに A_EventInfo変数に格納される値を指定する。
省略時は、コールバック関数のアドレスとなる。
A_EventInfo変数は、OptionsFast が指定されていても、既存スレッドの変数を上書きしない。

戻り値[編集]

成功した場合、作成した関数のアドレスを返す。
FunctionName で指定した関数が存在しなかったり、引数の数や形式に問題があった場合などは、空の文字列が返される。

コールバック関数の仕様[編集]

コールバック関数は、DllCall()で呼び出す関数の引数に関数のアドレスを渡さなくてはならない場合などに使用する。
DLLの関数からコールバック関数が呼び出されると、 FunctionName で指定した関数がAutoHotkeyによって実行される。

引数[編集]

FunctionName で指定する関数には、最大31個までの引数を定義できる。
省略可能引数を使用すれば、ParamCount の違うコールバック関数から同じ関数を呼び出せるように出来る。
ByRef 型の引数は使用できない。

引数は、どのような型であっても、4バイト符号無し整数(0~4294967295=0xFFFFFFFF)に変換される。
本来の型が符号付き整数である場合は、以下のようにすることで符号付き整数に変換できる。

If (param > 0x7FFFFFFF)
    param := -(~param) - 1

本来の型が文字列値である場合は、下記のようなコードで内容を取り出せる。

VarSetCapacity(MyString, DllCall("lstrlen", "UInt", MyParameter))
DllCall("lstrcpy", "Str", MyString, "UInt", MyParameter)
VarSetCapacity(MyString, -1)	; 文字列バッファアドレスを渡したのでバッファ内の文字長を再カウントする

AHKL AutoHotkey_Lでは以下のようにする。

MyString := StrGet(MyParameter)

引数が構造体のアドレスの場合は、NumGet() を適宜利用して構造体内のデータを取得する必要がある。

AHKL [L60+] コールバック対象としたい関数が可変長引数を持つ場合、可変パラメタを格納した配列オブジェクトの最後の要素はコールバック関数自体のアドレスが追加される。これはスクリプト内で実際に引き渡された引数よりも一つ多くなっている。以下に例を挙げる:

callback := RegisterCallback("Func", "F", 1)	; Parameter count must be specified.
Func("Func was called directly.")		; Call Func directly.
DllCall(callback, float, 10.5)			; Call Func via callback.
Func(params*) {
    If IsObject(params)
        MsgBox % params[1]
    Else
        MsgBox % NumGet(params+0, 0, "float")
}

多くのコールバック関数は Stdcal の呼び出し規約を利用しているため、引数の個数は固定である。
この場合は、コールバック関数の生成時に ParamCount をきちんと指定してやる必要がある。
64ビット版や32ビット版のCdeclを利用する場合は、ParamCount は省略可能である。
というのも、全ての任意パラメタがデフォルト値を受けとるので、params に格納のするためのアドレス計算をすることから除外されるためである。

戻り値[編集]

FunctionName で指定した関数は、Return で符号付き32ビット整数(-2147483648~4294967295) を返すことができる。
負の値は、4バイト符号無し整数として扱われ、それ以外は4バイト符号無し整数として扱われる。
有効な数値を返さなかった場合、コールバック関数の返値は0になる。

処理中の割り込みを抑止する[編集]

関数の最初にCriticalコマンドを実行することで、関数実行中に他のスレッドが割り込むのを抑制できる

FastモードとSlowモード[編集]

デフォルトのSlowモードでは、コールバック関数実行用の新規スレッドでの実行となり、SendModeDetectHiddenWindows 等はAutoExecuteセクションで設定した値となる。これはスレッド割り込みとなるので、呼び出し元のスレッドの固有情報は待避&復元がなされるので、コールバック関数でこれらを変更したとしても、ErrorLevelSendMode に影響を与えることはない。

一方 Fastモードでは、コールバック関数の実行は呼び出し元スレッド上で実行される。このため動作は高速となる。 元々の処理を一時中断してコールバック関数の処理を行うため、ErrorLevelSendMode, DetectHiddenWindows といったスレッド固有情報は、呼び出し元スレッドで変更したものとなっている。 加えて、コールバック関数内でこれらに変更を加えた場合は、その結果は呼び出し元スレッドに影響を与えることになる。

従って、Fastモードは、EnumWindows 関数や SQLite3 の sqlite3_exec 関数といったような、起動するタイミングがきちんと分かるような時のみに利用するのが望ましく、SetWindowsHookEx 関数のような任意のタイミングで呼ばれるようなものには利用すべきでない。

メモリ消費[編集]

コールバック関数を一つ生成するごとに、32バイト程度のメモリを消費する。
不特定多数のコールバック関数を生成する場合は、不要になった時点で DllCall("GlobalFree", UInt, MyCallBackFunc)のようにして解放すること。

Related[編集]

DllCall(), OnMessage(), OnExit, OnClipboardChange, Sort's callback, Critical, Post/SendMessage, 関数, スレッド

Example(s)[編集]

; Example: The following is a working script that displays a summary of all top-level windows.
; For performance and memory conservation, call RegisterCallback() only once for a given callback:
If not EnumAddress	; Fast-mode is okay because it will be called only from this thread:
    EnumAddress := RegisterCallback("EnumWindowsProc", "Fast")
DetectHiddenWindows On	; Due to fast-mode, this setting will go into effect for the callback too.
; Pass control to EnumWindows(), which calls the callback repeatedly:
DllCall("EnumWindows", UInt, EnumAddress, UInt, 0)
MsgBox %Output%		; Display the information accumulated by the callback.
EnumWindowsProc(hwnd, lParam)
{
    global Output
    WinGetTitle, title, ahk_id %hwnd%
    WinGetClass, class, ahk_id %hwnd%
    If title
        Output .= "HWND: " . hwnd . "`tTitle: " . title . "`tClass: " . class . "`n"
    Return true		; Tell EnumWindows() to continue until all windows have been enumerated.
}
; Example: The following is a working script that demonstrates how to subclass a GUI window by
; redirecting its WindowProc to a new WindowProc in the script. In this case, the background
; color of a text control is changed to a custom color.
TextBackgroundColor := 0xFFBBBB	; A custom color in BGR format.
TextBackgroundBrush := DllCall("CreateSolidBrush", UInt, TextBackgroundColor)
Gui, Add, Text, HwndMyTextHwnd, Here is some text that is given`na custom background color.
Gui +LastFound
GuiHwnd := WinExist()
WindowProcNew := RegisterCallback("WindowProc", ""  ; "" to avoid fast-mode for subclassing.
    , 4, MyTextHwnd)		; Must specify exact ParamCount when EventInfo parameter is present.
WindowProcOld := DllCall("SetWindowLong", UInt, GuiHwnd, Int, -4  ; -4 is GWL_WNDPROC
    , Int, WindowProcNew, UInt)	; Return value must be set to UInt vs. Int.
Gui Show
Return
WindowProc(hwnd, uMsg, wParam, lParam)
{
    Critical
    global TextBackgroundColor, TextBackgroundBrush, WindowProcOld
    If (uMsg = 0x138 && lParam = A_EventInfo)  ; 0x138 is WM_CTLCOLORSTATIC.
    {
        DllCall("SetBkColor", UInt, wParam, UInt, TextBackgroundColor)
        Return TextBackgroundBrush  ; Return the HBRUSH to notify the OS that we altered the HDC.
    }
    ; Otherwise (since above didn't return), pass all unhandled events to the original WindowProc.
    Return DllCall("CallWindowProcA", UInt, WindowProcOld, UInt, hwnd, UInt, uMsg, UInt, wParam, UInt, lParam)
}
GuiClose:
ExitApp