Тема: AHK: можно ли исп-ть OnMessage() для отслеживания процессов?
Можно ли с помощью OnMessage() отследить появление новых процессов и отмирание старых?
Вы не вошли. Пожалуйста, войдите или зарегистрируйтесь.
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться
Можно ли с помощью OnMessage() отследить появление новых процессов и отмирание старых?
Нельзя. Подобный мониторинг осуществляется средствами WMI.
Нельзя. Подобный мониторинг осуществляется средствами WMI.
То есть на это нельзя поставить хук (не OnMessage)?
Во всяком случае, я такого хука не обнаружил.
Во всяком случае, я такого хука не обнаружил.
Печалька.
Подобный мониторинг осуществляется средствами WMI.
Я хотел уточнить термины, если что то осуществляется только средствами WMI, то АНК это уже никак не сможет перехватить?
Почему же, AHK может пользоваться средствами WMI через COM, здесь уже была куча примеров. Собственно, вот:
#Persistent
OnlyAHK := 0 ; чтобы отслеживать только процессы AutoHotkey.exe — 1, иначе — 0
oSvc := ComObjGet("winmgmts:")
ComObjConnect(createSink := ComObjCreate("WbemScripting.SWbemSink"), "ProcessCreate_")
ComObjConnect(deleteSink := ComObjCreate("WbemScripting.SWbemSink"), "ProcessDelete_")
Command := "Within 1 Where TargetInstance ISA 'Win32_Process'"
. (OnlyAHK ? " And TargetInstance.Name = 'AutoHotkey.exe'" : "")
oSvc.ExecNotificationQueryAsync(createSink, "select * from __InstanceCreationEvent " Command)
oSvc.ExecNotificationQueryAsync(deleteSink, "select * from __InstanceDeletionEvent " Command)
ProcessCreate_OnObjectReady(obj)
{
Process := obj.TargetInstance
TrayTip, Новый процесс, % "PID = " Process.ProcessID "`nИмя = " Process.Name "`nКомандная строка = " Process.CommandLine
}
ProcessDelete_OnObjectReady(obj)
{
Process := obj.TargetInstance
TrayTip, Завершён процесс, % "PID = " Process.ProcessID "`nИмя = " Process.Name "`nКомандная строка = " Process.CommandLine
}
teadrinker
Отлично.
Во всяком случае, я такого хука не обнаружил.
Видимо снова моё непонимание терминов. Сообщение получать можно. Значит я неправильно использовал слово хук в вопросе:?
То есть на это нельзя поставить хук (не OnMessage)?
Если понимать слово хук (hook), как понимает его Microsoft, то неправильно.
То есть на это нельзя поставить хук (не OnMessage)?
То есть в данном контексте правильнее спросить:
То есть AutoHotkey неможет получать сообщения о запуске и закрытии процессов?
---
Значит хук это не только извещение о событии, но "и его перехват (блокировка) на пути к адресату"?
В данном контексте правильнее спросить "Можно ли отслеживать создание/завершение процессов, используя AHK?". Хук в классическом понимании — это перехват сообщений (Message), а при создании/завершении процессов никаких сообщений не рассылается.
Что же касается хука, устанавливаемого посредством SetWinEventHook(), это немного отдельная тема, он действительно отслеживает именно события (без возможности перехвата), но в списке событий такого нет.
Ещё один, наверняка глупый вопрос:
Можно ли отслеживать создание/завершение процессов
...
а при создании/завершении процессов никаких сообщений не рассылается.
В чём отличие понятий - "отслеживать событие" и "получать сообщения о событии"?
Отслеживать — получать информацию о сообщениях (или перехватывать их), отправленных окнам чужих процессов, а получать можно сообщения, отправленные окнам своего процесса. "Сообщение о событии" — это неясный термин, есть просто сообщения, несущие какую-то информацию.
teadrinker
Понял. Спасибо за ЛикБез
Почему же, AHK может пользоваться средствами WMI через COM, здесь уже была куча примеров. Собственно, вот:
#Persistent OnlyAHK := 0 ; чтобы отслеживать только процессы AutoHotkey.exe — 1, иначе — 0 oSvc := ComObjGet("winmgmts:") ComObjConnect(createSink := ComObjCreate("WbemScripting.SWbemSink"), "ProcessCreate_") ComObjConnect(deleteSink := ComObjCreate("WbemScripting.SWbemSink"), "ProcessDelete_") Command := "Within 1 Where TargetInstance ISA 'Win32_Process'" . (OnlyAHK ? " And TargetInstance.Name = 'AutoHotkey.exe'" : "") oSvc.ExecNotificationQueryAsync(createSink, "select * from __InstanceCreationEvent " Command) oSvc.ExecNotificationQueryAsync(deleteSink, "select * from __InstanceDeletionEvent " Command) ProcessCreate_OnObjectReady(obj) { Process := obj.TargetInstance TrayTip, Новый процесс, % "PID = " Process.ProcessID "`nИмя = " Process.Name "`nКомандная строка = " Process.CommandLine } ProcessDelete_OnObjectReady(obj) { Process := obj.TargetInstance TrayTip, Завершён процесс, % "PID = " Process.ProcessID "`nИмя = " Process.Name "`nКомандная строка = " Process.CommandLine }
А интересный код. К сожалению, я не знаю о существовании каких-либо туториалов (даже на английском) по работе с WMI, с COM и с объектами (про работу с объектами есть обычная документации, но она не изобилует примерами и после её тщательного прочтения - я ничего не понял) в AHK, потому прошу ещё помочь.
Я все эти вопросы задаю с целью улучшить работу своего MasterScript скрипта, который сейчас работает таким образом:
есть label (вызываемый периодически, по таймеру) который содержит For Process In ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process"), которая наполняет 2 массива данными: 1 для информации обо всех процессах, 1 для информации только о запущенных ahk-скриптах.
Зачем мне нужны эти массивы? Коротко объяснить не получится, надо сначала рассказать что дальше делает скрипт с этими данными:
Затем из "скриптового" массива собираются PIDы всех процессов в 1 однострочную переменную, где все PIDы отделены друг от друга трубой "|".
Эта переменная каждый раз бэкапится и потом производится сравнение текущей "свежей" версии этой переменной с её бэкапом.
При этом выявляется разницу между ними (появились ли новые PIDы? умерли ли какие-то из старых PIDов?).
По этой выявленной разнице скрипт принимает решение обновлять ли ListView со списком запущенных скриптов, и если обновлять, то - как это сделать? (нужно ли удалить какие-то строки? если да, то какие? нужно ли добавить новых строк? если да, то с каким содержимым?)
В случае с удалением строк - проблем нет, а вот в случае с добавлением новых - информация об их содержимом как раз и берётся из "скриптового" массива.
В принципе, на этом этапе мне уже "скриптовый" массив и не нужен, если я буду брать информацию из твоей функции ProcessCreate_OnObjectReady(obj), с этим я определился.
Тут проблема с другим массивом: "общепроцессный" массив нужен для работы фичи "Process Assistant" (про работу этой фичи рассказано в теме моего скрипта) и мне нужно, чтобы этот массив всегда содержал актуальную информацию, т.е. чтобы твои функции ProcessDelete_OnObjectReady(obj) и ProcessCreate_OnObjectReady(obj) соответствующим образом обновляли этот массив.
Как в таком случае правильно переписать функции ProcessDelete_OnObjectReady(obj) и ProcessCreate_OnObjectReady(obj), чтобы они обновляли существующий массив со списком данных обо всех процессах?
Массив многомерный: состоит из [index, "pid"] := Process.ProcessId и [index, "exe"] := Process.ExecutablePath.
Ключевое поле в массиве - связка [index, "pid"] процесса.
Если это удобней делать не массивами, а объектами (хотя и массивы, вроде бы, - это частный случай объектов) - покажи, пожалуйста, как их наполнять и как с ними работать (получать содержимое определённой ячейки).
как правильно переписать функции ProcessDelete_OnObjectReady(obj) и ProcessCreate_OnObjectReady(obj), чтобы они обновляли существующий массив со списком данных обо всех процессах?
Массив многомерный: состоит из [index, "pid"] := Process.ProcessId и [index, "exe"] := Process.ExecutablePath.
Ключевое поле в массиве - связка [index, "pid"] процесса.
Если это удобней делать не массивами, а объектами (хотя и массивы, вроде бы, - это частный случай объектов) - покажи, пожалуйста, как их наполнять и как с ними работать (получать содержимое определённой ячейки).
Не совсем ясна суть вопроса. Если ты знаешь, как получить PID и ExecutablePath, то в чём проблема занести/удалить их в свой массив?
Проблема более не актуальна.
Я плохо умел работать с массивами, но сейчас, вроде, стал получше понимать работу объектов.
teadrinker, возник новый вопрос по предложенному тобой коду:
если я убиваю/запускаю пачку процессов сразу, то как работают эти функции (ProcessCreate_OnObjectReady и ProcessDelete_OnObjectReady)? Судя по тому, что ни одно из сообщений не пропускается - я предполагаю, что существует какая-то очередь, где они дожидаются момента, когда скрипт их обработает. Так ли это?
Я повесил во внутрь этих функций вызов другой функции, которая парсит массивы и делает кучу сравнений, обновляет массивы и список (ListView) и т.п. и я боюсь, что при объёмных массивах на всё это будет уходить достаточно много времени.
Во-первых, я боюсь, что это вызовет какие-то конфликты, т.к. я не знаю в какой из моментов начинает обрабатываться следующий вызов ProcessCreate_OnObjectReady или ProcessDelete_OnObjectReady из существующей очереди.
Во-вторых, вызов этой функции (которая парсит массивы и т.п.) - в любом случае лучше бы делать не каждый раз, при обработке вызовов ProcessCreate_OnObjectReady/ProcessDelete_OnObjectReady, а один раз спустя секунду после обработки последнего из всей пачки этих вызовов. Т.е. если в течение секунды с момента обработки предыдущего вызова - появляется новый, то таймер надо заново "завести" на ожидание секунды уже с момента этого вызова и т.д.
Можно ли это как-то сделать? Как?
Просто не хочется заводить постоянный таймер с выполнением периодических проверок.
Как идея по поводу того, как это реализовать у меня есть только такое:
добавить в во внутрь ProcessCreate_OnObjectReady и ProcessDelete_OnObjectReady
thisTime := A_Now
SetTimer, timer, 500
и отдельно создать ярлык примерно следующего содержания для вызова этим таймером:
timer:
if (thisTime+500 < A_Now)
sleep 500
Else
funcCall()
SetTimer, timer, off
Return
Но я не уверен, что во время этого Sleep 500 скрипт не будет вообще приостановлен, а сможет обрабатывать очередь вызовов тех функций.
Или это делается иначе? Признаться, я не до конца понимаю как работает пример 3 из справки, но на вид - это как раз аналогично моей проблеме.
Может Critical тут как-то поможет?
Вроде научился правильно использовать Critical.
Но вот новый вопрос:
у меня на появление 1 нового процесса ProcessCreate_OnObjectReady() срабатывает несколько раз.
Аналогичная проблема с WM_DEVICECHANGE() решилась добавкой фильтрации по hwnd (т.е. он ловил и аналогичные сообщения, которые отправлялись чужим окнам (чужих приложений)), а как здесь добавить аналогичную фильтрацию?
у меня на появление 1 нового процесса ProcessCreate_OnObjectReady() срабатывает несколько раз.
Приведите минимальный код, который демонстрирует проблему, у меня срабатывает как положено, один раз на каждый процесс.
Аналогичная проблема с WM_DEVICECHANGE() решилась добавкой фильтрации по hwnd (т.е. он ловил и аналогичные сообщения, которые отправлялись чужим окнам (чужих приложений))
Нет, такого не было. OnMessage() отлавливает только сообщения, посланные окнам своего процесса.
Нет, такого не было. OnMessage() отлавливает только сообщения, посланные окнам своего процесса.
А, точно-точно. Там немножко другой нюанс был: там сообщения дублировались из-за того, что у моего скрипта не одно, а несколько окон и сообщение отправляется каждому окну.
Исправили тогда это всё заменой WM_DEVICECHANGE(wp, lp) на WM_DEVICECHANGE(wp, lp, msg, hwnd) + проверкой if (hwnd = A_ScriptHwnd).
Касательно минимального кода - увы, это очень проблематично, но я в функцию ProcessCreate_OnObjectReady(Obj){} добавил Process := obj.TargetInstance + outputdebug, % Process.ExecutablePath и вижу, что при однократном запуске одного скрипта - функция вызывается трижды. У меня в скрипте как раз 3 окна: иконка в трее, основное GUI окно и дополнительное GUI окно.
У меня в скрипте как раз 3 окна: иконка в трее, основное GUI окно и дополнительное GUI окно.
А причём тут иконка в трее?
#NoTrayIcon
DetectHiddenWindows on
WinGet, hwnd, List, % "ahk_pid " DllCall("GetCurrentProcessId")
Loop % hwnd
{
WinGetClass, Class, % "ahk_id" hwnd%A_Index%
WinGetTitle, Title, % "ahk_id" hwnd%A_Index%
MsgBox % Title "`n" Class
}
serzh82saratov, я и с этим ошибся, но по большому счёту это и не важно. Не иконка в трее, а просто у любого процесса есть своё встроенное окно. + доп. окна создаются если использовать, например gdi+ полотна (без привязки к каким-то окнам).
Вопрос в том как бы отфильтровать, дублирующиеся сообщения у вышеобозначенной функции? На MSDN поискал - но ничего не понял / не нашёл / вроде как нашёл, что у этой функции нет аттрибута hwnd. Help!
К количеству окон у процесса этот метод не имеет отношения.
Чего-то я очень не внимательный. Визуально текст оказался похожим, а на самом деле это разные оповещения были от разных функций (имена длинные и визуально похожие). Всё срабатывает однократно, извините за беспокойство.
Я долго не мог понять почему у меня в скрипте в обновляемом списке где должны отображаться только живые процессы - собирались и "зомби", пока не начал отладку. Оказалось, что дело в этом куске кода:
ProcessDelete_OnObjectReady(obj) { Process := obj.TargetInstance TrayTip, Завершён процесс, % "PID = " Process.ProcessID "`nИмя = " Process.Name "`nКомандная строка = " Process.CommandLine }
В этой функции я добавил вывод отладочной информации (.processID, .commandLine и .executablePath процессов) через Outputdebug.
Оказалось, что в некоторых случаях при смерти процессов ahk-скриптов (у которых при запуске были заполнены все данные: и .processID, и .commandLine, и .executablePath) почему-то .commandLine и .executablePath становятся пустыми.
Такое происходит не всегда, довольно редко, с одними и теми же скриптами, с которыми в другие разы всё ок. Закономерностей и взаимосвязей этого явления с чем-либо не обнаружил.
Страницы 1
Чтобы отправить ответ, вы должны войти или зарегистрироваться