Тема: AHK: Как перехватить все перемещения мыши и все нажатия клавиш?
Нужно среагировать на все действия мыши и клавиатуры. Как это сделать? Хотя бы только мыши.
Вы не вошли. Пожалуйста, войдите или зарегистрируйтесь.
Серый форум → Общение → AutoHotkey → AHK: Как перехватить все перемещения мыши и все нажатия клавиш?
Чтобы отправить ответ, вы должны войти или зарегистрироваться
Нужно среагировать на все действия мыши и клавиатуры. Как это сделать? Хотя бы только мыши.
Нужно среагировать на все действия мыши и клавиатуры. Как это сделать? Хотябы только мыши.
OnMessage, если этого мало, то нужно использовать хуки, а для этого нужна dll.
что-то не работает...
#SingleInstance, Force
#Persistent
OnMessage(WM_MOUSEMOVE, MyMessageMonitor)
MyMessageMonitor(wParam, lParam, msg, hwnd)
{
MsgBox %wParam%, %lParam%, %msg%, %hwnd%
}
return
что-то не работает...
Во-первых имя функции нужно брать в кавычки, во-вторых WM_MOUSEMOVE будет приходить только когда мышь перемещается над рабочей областью окна(тут основное окно нулевого размера и невидимое).
то нужно использовать хуки, а для этого нужна dll.
Нет, для отслеживания событий мыши и клавиатуры как раз можно обойтись без dll, попозже напишу.
teadrinker, ок, ждём. Только без циклической проверки координат.
Клавиатурный хук:
#Persistent
OnExit, Exit
hHookKeybd := DllCall("SetWindowsHookEx"
, Int, WH_KEYBOARD_LL := 13
, UInt, RegisterCallback("LowLevelKeyboardProc", "Fast")
, UInt, DllCall("GetModuleHandle", UInt, 0)
, UInt, 0)
Return
Exit:
DllCall("UnhookWindowsHookEx", UInt, hHookKeybd)
ExitApp
LowLevelKeyboardProc(nCode, wParam, lParam)
{
Critical
SetFormat, Integer, H
ToolTip, % wParam . "| vkCode: " . NumGet(lParam+0, 0) . " scCode: " . NumGet(lParam+0, 4)
. " Extended: " . NumGet(lParam+0, 8) & 1 . " Time: " . NumGet(lParam+0, 12)
Return DllCall("CallNextHookEx", UInt, 0, Int, nCode, UInt, wParam, UInt, lParam)
}
Мышиный хук:
OnExit, Exit
hHookKeybd := DllCall("SetWindowsHookEx"
, Int, WH_MOUSE_LL := 14
, Int, RegisterCallback("LowLevelMouseProc", "Fast")
, UInt, DllCall("GetModuleHandle", UInt, 0)
, UInt, 0)
Return
Exit:
DllCall("UnhookWindowsHookEx", Uint, hHookKeybd)
ExitApp
LowLevelMouseProc(nCode, wParam, lParam)
{
ToolTip, % "Message = " . wParam
. "`nMouse_X = " . NumGet(lParam+0, 0)
. "`nMouse_Y = " . NumGet(lParam+0, 4)
. "`nExtended = " . (NumGet(lParam+0, 8)&0xFFFF0000)>>16
. "`nTime = " . NumGet(lParam+0, 16)
Return DllCall("CallNextHookEx", UInt, 0, Int, nCode, UInt, wParam, UInt, lParam)
}
Esc::ExitApp
teadrinker, работает.
Прошу занести это в "Коллекцию скриптов"
А на семёрке работает?
А на семёрке работает?
Да, и прав администратора не требует. Единственное отличие от предыдущих версий- в случае, если обработчик не укладывается в выделенное системой время на обработку, он вообще удаляется из очереди(ранее в таких случаях просто вызывался следующий обработчик).
Александр_ пишет:то нужно использовать хуки, а для этого нужна dll.
Нет, для отслеживания событий мыши и клавиатуры как раз можно обойтись без dll, попозже напишу.
Однако, написал вариант с использованием dll. Но меня устраивает, то что надо.
Нет, имеется в виду, что для функции-обработчика событий придётся писать отдельную dll, а в моих вариантах она в самом скрипте.
Прошу занести это в "Коллекцию скриптов"
Согласен. А этот скрипт: AHK: Отслеживание времени бездействия мыши. удалите, или перенесите в раздел "Как не надо делать"
А почему, разве не работает?
Там работает за счет постоянной проверки координат, бесполезная работа впустую когда мышь не двигается. Работает, но не оптимально. То же самое можно сделать на механизме из этой темы, гораздо выгоднее.
Не вижу необходимости удалять работоспособный скрипт.
Если они туда попадают по такому критерию, тогда ладно. Но мне кажется этот вариант лучше чем тот.
Ну, можно дополнить. Первый вариант для начинающего будет понятней.
D_Pavel, (выделение моё):
Ну, можно дополнить. Первый вариант для начинающего будет понятней.
Исключать же скрипт из Коллекции имеет смысл, если он содержит явные ошибки.
К сожалению, не работает на AHK_L x64; win7 x64.
Попробуй так:
#Persistent
OnExit, Exit
hHookKeybd := DllCall("SetWindowsHookEx"
, Int, WH_KEYBOARD_LL := 13
, UInt, RegisterCallback("LowLevelKeyboardProc", "Fast")
, UInt, DllCall("GetModuleHandle", UInt, 0)
, UInt, 0)
Return
Exit:
DllCall("UnhookWindowsHookEx", UInt, hHookKeybd)
ExitApp
LowLevelKeyboardProc(nCode, wParam, lParam)
{
Critical
SetFormat, Integer, H
ToolTip, % wParam . "| vkCode: " . NumGet(lParam+0, 0, "UInt") . " scCode: " . NumGet(lParam+0, 4, "UInt")
. " Extended: " . NumGet(lParam+0, 8, "UInt") & 1 . " Time: " . NumGet(lParam+0, 12, "UInt")
Return DllCall("CallNextHookEx", UInt, 0, Int, nCode, UInt, wParam, UInt, lParam)
}
OnExit, Exit
hHookKeybd := DllCall("SetWindowsHookEx"
, Int, WH_MOUSE_LL := 14
, Int, RegisterCallback("LowLevelMouseProc", "Fast")
, UInt, DllCall("GetModuleHandle", UInt, 0)
, UInt, 0)
Return
Exit:
DllCall("UnhookWindowsHookEx", UInt, hHookKeybd)
ExitApp
LowLevelMouseProc(nCode, wParam, lParam)
{
ToolTip, % "Message = " . wParam
. "`nMouse_X = " . NumGet(lParam+0, 0, "UInt")
. "`nMouse_Y = " . NumGet(lParam+0, 4, "UInt")
. "`nExtended = " . (NumGet(lParam+0, 8, "UInt")&0xFFFF0000)>>16
. "`nTime = " . NumGet(lParam+0, 16, "UInt")
Return DllCall("CallNextHookEx", UInt, 0, Int, nCode, UInt, wParam, UInt, lParam)
}
Esc::ExitApp
Протестировано, tooltip'а нет.
Для GetModuleHandle тоже надо Fast указать.
При DllCall("GetModuleHandle", UInt, 0, "Fast") клавиатурный хук работает так же как и на ahk_l x86; winxp.
Хук мыши же приводит к неподвижности курсора при первом клике.
Для GetModuleHandle тоже надо Fast указать.
Не совсем понял, как это?
Может, нужно учесть наличие Unicode и ANSI версий GetModuleHandle()? creature.ws, попробуй в последнем моём варианте заменить GetModuleHandle на GetModuleHandleW.
При DllCall("GetModuleHandle", UInt, 0, "Fast") клавиатурный хук работает так же как и на ahk_l x86; winxp.
Хук мыши же приводит к неподвижности курсора при первом клике.
А у меня не приводит. Хотя вообще движется курсор как слегка больной. Я бы так работать не хотел, конечно.
Win7/64 HE SP1, AHK_64 1.1.05.04
Да откуда ты этот Fast в DllCall() взял?
teadrinker
Я что-то сам не пойму. Я подумал, что Fast — это соглашение вызова на 64 битах (fastcall). Но судя по описанию RegisterCallback, это не то. Да и по идее AHK_64 по умолчанию должен fastcall использовать. К сожалению, я с ним дела практически не имел, использую AHK_L 32 Unicode.
Чудеса: я сейчас вместо "Fast" поставил чисто от балды "Abc" — тоже помогает!
Может, за "Cdecl" принимает? (Правда, я не очень понимаю, что это такое).
Понял, что тут работает: при всех этих левых добавках GetModuleHandle возвращает пустое значение. Можно вместо него просто передать 0.
Кстати, вроде там и нужен ноль по идее.
hMod
[in] Handle to the DLL containing the hook procedure pointed to by the lpfn parameter. The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by the current process and if the hook procedure is within the code associated with the current process.dwThreadId
[in] Specifies the identifier of the thread with which the hook procedure is to be associated. If this parameter is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread.
Может, логика такая, что all existing threads running in the same desktop as the calling thread включают и саму calling thread, т.е. ты как бы её указал. К тому же the hook procedure ведь не в DLL.
teadrinker
Может, за "Cdecl" принимает? (Правда, я не очень понимаю, что это такое).
Cdecl отличается от stdcall только тем, что вызванная функция при выходе не меняет указатель стека так, чтобы переданные ей параметры оказались в свободной части стека. За неё это должен сделать вызывающий код. В Windows API cdecl практически нет. Только функцию wsprintf знаю. Ну и функции в msvcrt.dll, но это ведь не часть API, а "C runtime".
Во всяком случае, у меня на XP c нулём вместо GetModuleHandle не прокатывает ни тот, ни другой хук.
При DllCall("GetModuleHandleW", UInt, 0) не работает.
При передаче нуля - tooltip показывается, 1-2 клика обрабатываются - курсор останавливается.
После закрытия скрипта курсор рывком перемещается в точку куда был бы переведен, не будь он обездвижен
teadrinker
Да, на ХР не работает с нулём. Остаётся, видимо, проверять версию АНК и использовать либо hModule, либо нуль.
creature.ws
У Вас винда кривая. Наверно, не все 64 бита положили, пересчитайте.
Возможно, ещё какой-то скрипт работает и мешается под ногами?
Видимо так
Проверил после перезагрузки при отключенном основном скрипте, более в системе посторонних хуков мыши никто ставить вроде бы не должен.
Service Pack 1 Build 7601, x64-based, 68 Hotfix(s) Installed; Ahk_L U x64 1.1.05.05
А можно как нибудь перехватывая нажатия клавиатуры, узнавать не только код клавиши, а и буквы, при соответствующей раскладке клавиатуры?
Сохранение буфера обмена, выполнаю с помощью подпрограмы OnClipboardChange, удобно вести историю копируемого, но вот хотелось бы сохранять у себя на компьютере, еще и все, что набирается на клавиатуре, а стороннее ПО использовать для этого не охота.
Вот, а также была соответствующая тема в разработке, ищи, там подробнее.
Огромное вам спасибо всем за скрипты!!!
На Windows 7 x64 Autohotkey_L 1.1.07.01 работает
Жаль, на AHK_L x64 мышиный хук приводит к обездвиживанию курсора при первом-втором клике.
Посмотри, может там где-то "UInt" нужно заменить на "UPtr".
Спасибо за рекомендацию, попробовав вариант с заменой UInt на Ptr во всех подходящих местах и не получив желаемого результата, решил таки проверить скрипт на свежеустановленной на вм аналогичной системе. Работает в оригинальном виде при hMod = 0
Вероятно, подтвердилось сказанное выше:
creature.ws
У Вас винда кривая.
Мои извинения за ложные багрепорты
Есть одно замечание по скрипту. Насколько мне известно, в функциях низкоуровневого хука следует проверять значение аргумента nCode, если nCode < 0, процедура должна немедленно вызвать CallNextHookEx и вернуть значениe этой функции, обработка же хука осуществляется только при nCode >= 0.
afinagen да, именно так и об этом пишут на MSDN:
Может кому пригодится у кого сложности как у меня со скриптингом - функция для автохоткея чтобы выводить полученные сканкоды и виртуалкеи в формате в котором работает сам автохоткей
необходимые параметры - сам код, далее extended информация (0 или 1 - добавляется 3ий разряд к сканкоду) и сам мод - SC или VK
KeyCodeGetHex(number,extend,mode) {
arrMods:=["16","1"]
if (mode="sc") {
SetFormat, Integer, D
result:=extend+0
SetFormat, Integer, H
}
Loop, 2
{
tmp:=Mod(number//arrMods[A_Index],16)
StringTrimLeft, tmp, tmp, 2
result.=tmp
}
Return mode . result
}
Есть одно замечание по скрипту. Насколько мне известно, в функциях низкоуровневого хука следует проверять значение аргумента nCode, если nCode < 0, процедура должна немедленно вызвать CallNextHookEx и вернуть значениe этой функции, обработка же хука осуществляется только при nCode >= 0.
В моём примере CallNextHookEx возвращается в любом случае. Что же до ситуации, когда nCode < 0, то вот такой скрипт
OnExit, Exit
hHookKeybd := DllCall("SetWindowsHookEx"
, Int, WH_MOUSE_LL := 14
, Int, RegisterCallback("LowLevelMouseProc", "Fast")
, UInt, DllCall("GetModuleHandle", UInt, 0)
, UInt, 0)
Return
Exit:
DllCall("UnhookWindowsHookEx", Uint, hHookKeybd)
ExitApp
LowLevelMouseProc(nCode, wParam, lParam)
{
ToolTip % nCode
if nCode < 0
SoundBeep
Return DllCall("CallNextHookEx", UInt, 0, Int, nCode, UInt, wParam, UInt, lParam)
}
Esc::ExitApp
показывает, что подобного в данном варианте установки хука просто не происходит.
Прошу подскажите, пожалуйста, как сделать, чтобы эти низкоуровненые хуки не ловили то, что шлет Autohotkey?
Ты имеешь в виду нажатия клавиш, или что?
допустим мы сделали Hotkey в скрипте - на F1 у нас вместо F1 жмется F2, тогда хуки это увидят так: F1 Down F2 Down F2 Up F1 Up, то есть хуки видят не только физические нажатия пользователя по клаве, но и виртуальные нажатия посылаемые автохоткеем, вот подскажите очень очень прошу, как сделать чтобы эти виртуальные нажатия не хукались?
if(NumGet(lParam|0, 8, "uint")&16)
...
огромное спасибо
А зачем нужны эти хуки? Можно этот процесс убрать как-то? - комп тормозит, и там конфликты с другими приложениями, что-ли, могут возникнуть?
А зачем нужны эти хуки?
Тема по хукам, код обновлён для использования с любыми версиями AHK_L (но не AHK-basic). Если код написан верно, никаких конфликтов и тормозов не должно быть.
Удалено
Удалено
В таком случае я прошу Вашего совета, Что мне делать, Создавать новую тему?, писать в существующую?
Создайте новую тему.
OFF: Да, есть такой "скачок" времени.
Я видел. Судя по тому, что некоторое время хостинг отпинывал запросы на обслуживание — проблемы были на его стороне.
Borodas, вопросы вроде "Помогите плиз написать скрипт" задавать только в новой теме!
ОК спасибо! Создаю новую тему. Модератора или администратора просьба удалить мои посты из этой темы.
teadrinker
Клавиатурный хук в коллекции неправильно определяет SC код некоторых кнопок, например: RShift, NumpadEnter.
Да, исправлю.
Надо ещё мышиный подправить, наверное.
А с мышиным что не так?
Ну просто сделать тогда уж в том же стиле, что и предыдущий, с таймером и выводом сообщений.
А Я подумал, что что-то не так определяло.
Нет, но в любом случае обработку событий лучше по таймеру делать, чтобы не возникало проблем.
Нет, но в любом случае обработку событий лучше по таймеру делать, чтобы не возникало проблем.
SetTimer, EventHandling, -10
Но, если за 10 мсек произойдёт например 2-3 события (такое возможно например при искуственном вводе), то последнее событие обновит таймер предыдущих, и мы не узнаем о них.
Да, но если события будут происходить до завершения обработки предыдущих в хуке, будут задержки, а иногда и сбои, залипания клавиш, и ещё всякие неприятности.
Ещё такой вопрос: можно ли изменять статус клавиши? Например зажали "RButton", и надо теперь чтобы при отпускании её, "RButton Up" не прошло в активное окно, но также чтобы она не осталась залипшей.
Да как-то не задумывался. А для чего это конкретно нужно?
Ну например организовать RButton & LButton хоткей.
Не знаю, поэкспериментировать нужно. Помню, что когда подобное без хука пытался сделать, какие-то траблы вечно вылезали.
Ещё раз попробовал решить вопрос, не пойму в чём дело, что то не так с lParam в массиве?
OnExit, Exit
hHookKeybd := DllCall("SetWindowsHookEx"
, Int, WH_KEYBOARD_LL := 13
, Ptr, RegisterCallback("LowLevelKeyboardProc", "Fast")
, Ptr, DllCall("GetModuleHandle", UInt, 0, Ptr)
, UInt, 0, Ptr)
Return
Exit:
DllCall("UnhookWindowsHookEx", Ptr, hHookKeybd)
ExitApp
LowLevelKeyboardProc(nCode, wParam, lParam)
{
static oMem := [], q := 0, w := 0
, oMsg := {0x100: "WM_KEYDOWN", 0x101: "WM_KEYUP", 0x104: "WM_SYSKEYDOWN", 0x105: "WM_SYSKEYUP"}
oMem.Push([wParam, lParam]), ++q
SetTimer, EventHandling, -10 ; обработку событий "повесим" на таймер, чтобы не создавать помех в работе клавиатуры
Return DllCall("CallNextHookEx", Ptr, 0, Int, nCode, Ptr, wParam, Ptr, lParam)
EventHandling:
While (oMem[1] != "")
{
wp := oMem[1][1], lp := oMem[1][2]
oMem.RemoveAt(1)
SetFormat, IntegerFast, H
msg := wp . "" ; добавляем "", чтобы шестнадцатеричное число интерпретировалось,
vk := NumGet(lp + 0, "UInt") . "" ; как строка, и не превратилось в десятичное после смены формата
ext := NumGet(lp + 8, "UInt")
sc := NumGet(lp + 4, "UInt") | (ext & 1) << 8 . ""
SetFormat, IntegerFast, D
time := NumGet(lp + 12, "UInt")
ToolTip % "Message = " . oMsg[msg]
. "`nvkCode = " . vk
. "`nscCode = " . sc
. "`nTime = " . time
. "`nPhysical = " . (ext & 16 ? "Not" : "Yes")
. "`n " q " " ++w " " oMem.MaxIndex()
Sleep 100
}
Return
}
Ещё актуально? В чём там проблема?
vk, sc то есть, то пустые, и неправильные.
OnExit, Exit
hHookKeybd := DllCall("SetWindowsHookEx"
, Int, WH_KEYBOARD_LL := 13
, Ptr, RegisterCallback("LowLevelKeyboardProc", "Fast")
, Ptr, DllCall("GetModuleHandle", UInt, 0, Ptr)
, UInt, 0, Ptr)
Return
Exit:
DllCall("UnhookWindowsHookEx", Ptr, hHookKeybd)
ExitApp
LowLevelKeyboardProc(nCode, wParam, lParam)
{
static oMem := [], q := 0, w := 0
, oMsg := {0x100: "WM_KEYDOWN", 0x101: "WM_KEYUP", 0x104: "WM_SYSKEYDOWN", 0x105: "WM_SYSKEYUP"}
arr := {}
arr.msg := Format("{:#x}", wParam)
arr.vk := Format("{:#x}", NumGet(lParam + 0, "UInt"))
ext := NumGet(lParam + 8, "UInt") & 1
arr.sc := Format("{:#x}", NumGet(lParam + 4, "UInt") | ext << 8)
arr.ext := ext
arr.time := NumGet(lParam + 12, "UInt")
oMem.Push(arr), q++
SetTimer, EventHandling, -10
Return DllCall("CallNextHookEx", Ptr, 0, Int, nCode, Ptr, wParam, Ptr, lParam)
EventHandling:
while oMem[1] != "" {
msg := oMem[1].msg
vk := oMem[1].vk
sc := oMem[1].sc
ext := oMem[1].ext
time := oMem[1].time
oMem.RemoveAt(1)
ToolTip % "Message = " . oMsg[msg]
. "`nvkCode = " . vk
. "`nscCode = " . sc
. "`nTime = " . time
. "`nPhysical = " . (ext & 16 ? "Not" : "Yes")
. "`n " q " " ++w " " oMem.MaxIndex()
Sleep 100
}
Return
}
Esc::ExitApp
lParam в функции — локальная переменная, она существует только до Return функции, и извлекать что-либо из неё по таймеру не имеет смысла.
извлекать что-либо из неё по таймеру не имеет смысла
Не до конца понимаю. lParam это просто число. Число заносится в массив как значение. Дошли до Return, lParam исчезла - это понятно. Но её значение в массиве куда исчезает? И почему wParam в массиве остаётся?
Это не «просто число», это указатель на определённые данные в памяти, которые мы извлекаем. Данные по этому указателю валидны только на время выполнения функции, непосредственно в которую был послан указатель. Как только она выполнилась (дошла до Return), данные по этому адресу могут замещаться новой информацией (само значение указателя никуда не исчезает, можешь проверить). А вот wParam в данном случае — это как раз просто число.
определённые данные в памяти, которые мы извлекаем
Ок. Почему нельзя просто извлечь эти данные, и занести в массив, для последующей их обработки уже по таймеру?
Почему нельзя? В моём коде как раз так и делается.
Это я понимаю. А как то "целиком" эти данные можно поместить в переменную?
Можно прочитать участок памяти в статическую переменную, попозже напишу.
OnExit, Exit
hHookKeybd := DllCall("SetWindowsHookEx"
, Int, WH_KEYBOARD_LL := 13
, Ptr, RegisterCallback("LowLevelKeyboardProc", "Fast")
, Ptr, DllCall("GetModuleHandle", UInt, 0, Ptr)
, UInt, 0, Ptr)
Return
Exit:
DllCall("UnhookWindowsHookEx", Ptr, hHookKeybd)
ExitApp
Esc::ExitApp
LowLevelKeyboardProc(nCode, wParam, lParam)
{
static oMem := [], q := 0, w := 0, lParamData
, oMsg := {0x100: "WM_KEYDOWN", 0x101: "WM_KEYUP", 0x104: "WM_SYSKEYDOWN", 0x105: "WM_SYSKEYUP"}
VarSetCapacity(lParamData, Size := 16, 0)
DllCall("RtlMoveMemory", Ptr, &lParamData, Ptr, lParam, Ptr, Size)
oMem.Push([wParam, &lParamData]), ++q
SetTimer, EventHandling, -10 ; обработку событий "повесим" на таймер, чтобы не создавать помех в работе клавиатуры
Return DllCall("CallNextHookEx", Ptr, 0, Int, nCode, Ptr, wParam, Ptr, lParam)
EventHandling:
While (oMem[1] != "")
{
wp := oMem[1][1], lp := oMem[1][2]
oMem.RemoveAt(1)
msg := Format("{:#x}", wp)
vk := Format("{:#x}", NumGet(lp + 0, "UInt"))
ext := NumGet(lp + 8, "UInt")
sc := Format("{:#x}", NumGet(lp + 4, "UInt") | (ext & 1) << 8)
time := NumGet(lp + 12, "UInt")
ToolTip % "Message = " . oMsg[msg]
. "`nvkCode = " . vk
. "`nscCode = " . sc
. "`nTime = " . time
. "`nPhysical = " . (ext & 16 ? "Not" : "Yes")
. "`n " q " " ++w " " oMem.MaxIndex()
Sleep 100
}
Return
}
Спасибо! Вроде всё понятно.
Единственно что:
VarSetCapacity(lParamData, Size := 16, 0)
DllCall("RtlMoveMemory", Ptr, &lParamData, Ptr, lParam, Ptr, Size)
Если мы записали 16 байт в lParamData, занесли в массив адрес памяти, (при повторном вызове) не успев извлечь данные по этому адресу, очищаем её (VarSetCapacity) и снова пишем в неё данные. Что же тогда остаётся?
Если VarSetCapacity очищая lParamData, присваивает ей уже другой адрес, то как очищается память?
Если мы записали 16 байт в lParamData, занесли в массив адрес памяти, (при повторном вызове) не успев извлечь данные по этому адресу, очищаем её (VarSetCapacity) и снова пишем в неё данные. Что же тогда остаётся?
Вопрос резонный, адрес тот же, соответственно, этот метод не годится.
этот метод не годится.
Ммм... Не понял, работает же.
Просто каждый раз последние записанные данные используются.
Так, вроде, можно:
OnExit, Exit
hHookKeybd := DllCall("SetWindowsHookEx"
, Int, WH_KEYBOARD_LL := 13
, Ptr, RegisterCallback("LowLevelKeyboardProc", "Fast")
, Ptr, DllCall("GetModuleHandle", UInt, 0, Ptr)
, UInt, 0, Ptr)
Return
Exit:
DllCall("UnhookWindowsHookEx", Ptr, hHookKeybd)
ExitApp
Esc::ExitApp
LowLevelKeyboardProc(nCode, wParam, lParam)
{
static oMem := [], q := 0, w := 0, HEAP_ZERO_MEMORY := 0x8, hHeap := DllCall("GetProcessHeap", Ptr)
, oMsg := {0x100: "WM_KEYDOWN", 0x101: "WM_KEYUP", 0x104: "WM_SYSKEYDOWN", 0x105: "WM_SYSKEYUP"}
pHeap := DllCall("HeapAlloc", Ptr, hHeap, UInt, HEAP_ZERO_MEMORY, Ptr, Size := 16, Ptr)
DllCall("RtlMoveMemory", Ptr, pHeap, Ptr, lParam, Ptr, Size)
oMem.Push([wParam, pHeap]), ++q
SetTimer, EventHandling, -10 ; обработку событий "повесим" на таймер, чтобы не создавать помех в работе клавиатуры
Return DllCall("CallNextHookEx", Ptr, 0, Int, nCode, Ptr, wParam, Ptr, lParam)
EventHandling:
While (oMem[1] != "")
{
wp := oMem[1][1], lp := oMem[1][2]
msg := wp
vk := Format("{:#x}", NumGet(lp + 0, "UInt"))
ext := NumGet(lp + 8, "UInt")
sc := Format("{:#x}", NumGet(lp + 4, "UInt") | (ext & 1) << 8)
time := NumGet(lp + 12, "UInt")
DllCall("HeapFree", Ptr, hHeap, UInt, 0, Ptr, lp)
oMem.RemoveAt(1)
ToolTip % "Message = " . oMsg[msg]
. "`nvkCode = " . vk
. "`nscCode = " . sc
. "`nTime = " . time
. "`nPhysical = " . (ext & 16 ? "Not" : "Yes")
. "`n " q " " ++w " " oMem.MaxIndex()
Sleep 100
}
Return
}
Да, всё работает, спасибо за пример!
В LowLevelKeyboardProc() возможно узнать физическим было нажатие, или нет.
А возможно ли это в LowLevelMouseProc()?
А, кажется нашёл.
NumGet(LParam + 12, "Short") & 16
Только почему 16? Нас только первый и единственный бит интересует, так что &1 достаточно.
Ок. Просто сделеал по аналогии с LowLevelKeyboardProc, там &1 мало.
Что то у меня видимо с RegisterCallback нелады, не пойму почему при определении хук-процедуры как метода класса, идёт смещение параметров. В ToolTip видно что lParam теперь является не третьим а вторым.
Class.SetHook(1)
Return
Class Class {
HookProc(nCode, wParam, lParam) {
Critical
ToolTip % Format("vk{:X}", NumGet(wParam + 0, "UInt")) "`n" Format("vk{:X}", NumGet(lParam + 0, "UInt"))
Return DllCall("CallNextHookEx", "Ptr", 0, "Int", nCode, "UInt", wParam, "UInt", lParam)
}
SetHook(On) {
If On
Class.hHook := DllCall("SetWindowsHookEx" . (A_IsUnicode ? "W" : "A")
, "Int", 13 ; WH_KEYBOARD_LL
, "Ptr", RegisterCallback(Class.HookProc, "F")
, "Ptr", DllCall("GetModuleHandle", "UInt", 0, "Ptr")
, "UInt", 0, "Ptr")
Else
DllCall("UnhookWindowsHookEx", "Ptr", Class.hHook), Class.hHook := ""
}
}
Если в данном примере использовать функцию, которая является методом класса, в неё по идее должен быть забинден параметр this, как в примере к статье SetTimer, но такой синтаксис RegisterCallback() не поддерживает.
То есть без вариантов.
Ну, можно нырнуть в глубины winapi, и получить пойнтер на функцию через DllCall, но проще вывести за пределы класса.
Чтобы отправить ответ, вы должны войти или зарегистрироваться