1 (изменено: Malcev, 2011-09-18 12:52:30)

Тема: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Всем привет!
Объясню свой алгоритм работы.
Я из Adobe Premiere (программа видеомонтажа) экспортирую видеофайл формата avi.
Он экспортируется порядка 1 часа, после чего с помощью AHK загружается на фтп сервер.
Во время экспорта появляется окошко - "Rendering : ahk_class #32770", а при окончании  оно изчезает.
Скрипт такой

F12::
SetTitleMatchMode 2
WinWaitClose, Rendering : ahk_class #32770
Sleep, 5000
Run, C:\Program Files\TurboFTP\TurboFTP.exe G:\WATCHFOLDER\*.* ftp://........

Чтобы его запустить, мне соответственно необходимо нажать F12 во время экспорта.
Но к концу рабочего дня я до того устаю, что иногда забываю нажать эту кнопку.
Есть ли возможность запустить данный скрипт не F12, а например при событии создания *.avi файла в директории  G:\WATCHFOLDER
Спасибо!

PS Конечно можно было бы написать

F12::
SetTitleMatchMode 2
WinWait, Rendering : ahk_class #32770
WinWaitClose, Rendering : ahk_class #32770
Sleep, 5000
Run, C:\Program Files\TurboFTP\TurboFTP.exe G:\WATCHFOLDER\*.* ftp://........

Но так сделать не получится, так как помимо этого файла экспортируется ещё много чего другого в другие директории не для фтп.

2

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Можно периодически проверять наличие файла при помощи команды IfExist / IfNotExist.

3 (изменено: Malcev, 2011-09-18 16:07:06)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

SetTitleMatchMode 2
loop,
{
IfExist, E:\TEST\*.avi
WinWait, Rendering : ahk_class #32770
WinWaitClose, Rendering : ahk_class #32770
Sleep, 5000
Run, C:\Program Files\TurboFTP\TurboFTP.exe G:\WATCHFOLDER\*.* ftp://........
Return
IfNotExist, E:\TEST\*.avi
????????
}

Как я понимаю примерно так, только, что вписать вместо знаков вопросов, чтобы скрипт это залупил?

4

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Malcev пишет:

Как я понимаю примерно так, только, что вписать вместо знаков вопросов, чтобы скрипт это залупил?

Непонятен вопрос.

5

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Алгоритм скрипта, как я понимаю такой.
Создаём цикл, в котором будет выслеживаться есть файл ави в данной директории или нет.
Если есть, то прекращаем цикл, если нету, то начинаем заново.
Вот мне и непонятно, что написать вместо знаков вопроса, чтобы цикл начался заново?

6 (изменено: creature.ws, 2011-09-18 19:47:03)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Создаём таймер, который будет обращаться к метке, которая будет отслеживать есть файл ави в данной директории.
Если есть, то выполняем необходимое действие, приостанавливаем таймер на некое определённое время, по истечении которого вновь запускаем таймер.
«если файла нету» - не обрабатываем, так как действий при этом выполнять не нужно.

Вместо «приостановки таймера после необходимого действия» можно попробовать отследить статус выполнения задачи выполняемой при наличии файла ави в папке.

7

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Что-то не получается.

SetTimer, Timer, 1000
SetTitleMatchMode 2
Timer:
IfExist, E:\TEST\*.avi
WinWait, Rendering : ahk_class #32770
WinWaitClose, Rendering : ahk_class #32770
Sleep, 5000
Run, C:\Program Files\TurboFTP\TurboFTP.exe G:\WATCHFOLDER\*.* ftp://........
SetTimer, Timer, Off

8

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Наверное, так:

SetTimer, Timer, 1000
SetTitleMatchMode 2

Timer:
   Sleep, 5000
   IfNotExist, E:\TEST\*.avi
      Return
   WinWait, Rendering : ahk_class #32770
   WinWaitClose, Rendering : ahk_class #32770
   Run, C:\Program Files\TurboFTP\TurboFTP.exe G:\WATCHFOLDER\*.* ftp://........
   SetTimer, Timer, Off
   Return
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

9

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

А точнее, так:

SetTimer, Timer, 5000
SetTitleMatchMode 2

Timer:
   IfNotExist, E:\TEST\*.avi
      Return
   SetTimer, Timer, Off
   WinWait, Rendering : ahk_class #32770
   WinWaitClose, Rendering : ahk_class #32770
   Run, C:\Program Files\TurboFTP\TurboFTP.exe G:\WATCHFOLDER\*.* ftp://........
   Return
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

10

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Спасибо, но не получается.
Мне нужно чтобы скрипт отслеживал тот момент, когда файл появится в нужной директории и при появлении делал какие-либо действия , после чего заканчивал свою работу.
А в твоём скрипте, если файла нету, то он сразу заканчивает работу.

11

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

А да, сори, правильно так:

#Persistent
SetTimer, Timer, 5000
SetTitleMatchMode 2
 
Timer:
   IfNotExist, E:\TEST\*.avi
      Return
   SetTimer, Timer, Off
   WinWait, Rendering : ahk_class #32770
   WinWaitClose, Rendering : ahk_class #32770
   Run, C:\Program Files\TurboFTP\TurboFTP.exe G:\WATCHFOLDER\*.* ftp://........
   Return
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

12

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Сейчас всё заработало.
Спасибо!
Алгоритм скрипта понятен.
Но сам не допёр.

13

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Проверил этот скрипт в деле - сильно грузит систему.
Таймер поставил на 10 минут.

SetTimer, Timer, 600000

Нельзя ли что-то сделать в скрипте, чтоб не грузил.

14

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Нет, ничего не грузит, тут грузить нечему, что-то другое, наверное.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

15

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Мелкософт рекомендует в таких случаях использовать уведомления и я с ними солидарен:). Пример:

dir := "C:\Users\Александр\Desktop\test\cur" ; директория для мониторинга
FILE_NOTIFY_CHANGE_FILE_NAME :=  1            ; любые изменения связанные с именами файлов
INFINITE                     := -1            ; неограниченное время ожидания

x:=DllCall("FindFirstChangeNotificationA", "str", dir, "int", 0, "int", FILE_NOTIFY_CHANGE_FILE_NAME, "Cdecl int")
while DllCall("WaitForSingleObject", "int", x, "int", INFINITE, "Cdecl int") = 0
{
   MsgBox 4, , Был создан, удалён или переименован какой-то файл`r`nВы хотите продолжить мониторинг?
   IfMsgBox, No, break
   DllCall("FindNextChangeNotification", "int", x, "Cdecl int")
}
x:=DllCall("FindCloseChangeNotification", "int", x)

16

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Нет, ничего не грузит, тут грузить нечему, что-то другое, наверное.

Да вроде бы тоже странно, но когда скрипт запущен, тормозят другие AHK скрипты.
И в результате выводят всякие ошибки.

Александр_, завтра попробую твой пример скрипта.
Спасибо!

17

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Александр_, твой скрипт не работает.
Он не выводит никаких изменений при появлении  новых файлов в директории + он так грузит систему, что завершить работу скрипта можно только через task manager.

18

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Malcev пишет:

Александр_, твой скрипт не работает.
Он не выводит никаких изменений при появлении  новых файлов в директории + он так грузит систему, что завершить работу скрипта можно только через task manager.

Ну приехали...
Скрипт отлично работает, ищите ошибку у себя- может скрипт сохранён в unicode, может нет прав на чтение папки, а может и самой папки нету. Описания функций в рунете найти легко, например в книге Румянцева "работа с файлами в Win32"(раздел "основы работы с файлами", подраздел "уведомления об изменениях в файловой системе"). Ну а "грузить систему" он никак не может. Читайте про функцию WaitForSingleObject у того же Румянцева- в скрипте поток занят ожиданием сигнала от системы об изменениях в именах файлов, если нужно чтобы скрипт параллельно ещё что-то делал, то нужно запускать ожидание в отдельном потоке.

19

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Александр_ пишет:

Скрипт отлично работает,

Подтверждаю, работает как и заявлено.

Malcev пишет:

завершить работу скрипта можно только через task manager.

Это тоже подтверждаю. Но это вполне понятно, почему: нить сидит внутри «WaitForSingleObject» и ждёт возврата из функции. Александр_ об этом выше написал. Но как только мы оттуда вернулись — иконка в трэе становится доступной: попробуйте щёлкнуть по ней в момент, когда выводится сообщение от скрипта.

20

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Ошибка, которая на поверхности — использована ANSI-версия функции FindFirstChangeNotification() без возможности выбора, в то время, как AHK может быть и ANSI, и Unicode, это нужно учитывать.

Александр_ пишет:

если нужно чтобы скрипт параллельно ещё что-то делал, то нужно запускать ожидание в отдельном потоке.

AHK не поддерживает многопоточность.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

21

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Как-то так должно сработать, в т. ч. и выход:

dir =  ; указать директорию для мониторинга без кавычек
 
OnExit, Exit
 
Script =
(
   #NoTrayIcon
   dir = %dir%
   FILE_NOTIFY_CHANGE_FILE_NAME :=  1            ; любые изменения связанные с именами файлов
   INFINITE                     := -1            ; неограниченное время ожидания
    
   x:=DllCall("FindFirstChangeNotification" . (A_IsUnicode ? "W" : "A")
      , "str", dir, "int", 0, "int", FILE_NOTIFY_CHANGE_FILE_NAME, "Cdecl int")
   while DllCall("WaitForSingleObject", "int", x, "int", INFINITE, "Cdecl int") = 0
   {
      MsgBox 4, , Был создан, удалён или переименован какой-то файл``nВы хотите продолжить мониторинг?
      IfMsgBox, No, break
      DllCall("FindNextChangeNotification", "int", x, "Cdecl int")
   }
   x:=DllCall("FindCloseChangeNotification", "int", x)
)
 
FileDelete, %A_Temp%\Viewer.ahk
FileAppend, % Script, %A_Temp%\Viewer.ahk, % A_IsUnicode ? "UTF-8" : ""
Run, "%A_AhkPath%" "%A_Temp%\Viewer.ahk",,, PID
Return
 
Exit:
   FileDelete, %A_Temp%\Viewer.ahk
   Process, Close, %PID%
   ExitApp
 
F11:: ExitApp
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

22 (изменено: mih, 2011-09-27 19:45:19)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Что-то всё-же не так: и вариант Александр_а, и последний — подвешивают Проводник при попытке открыть txt файл левым кликом в редакторе EditPlus (а перетаскиванием работает). По f11 — Проводник восстанавливается. WinXP SP2, AHK_110401_ansi.

23

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Странно, у меня с Проводником всё в порядке.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

24

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

У меня тоже с проводником всё в порядке, хотя EditPlus не установлен.
Осталось проверить его в связке с другими моими скриптами.
В субботу проверю.
Спасибо!

25 (изменено: mih, 2011-09-29 00:06:26)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Выяснилось, что дело не конкретном редакторе, дело в наличии ключа HKEY_LOCAL_MACHINE\SOFTWARE\Classes\EditPlus 2.txt\shell\open\ddeexec  ("EditPlus 2.txt" — это т. наз. "тип", к которому у меня относится *.txt ). Если убираю эту ветку (просто переименовываю "ddeexec" в "ddeexec1") — Проводник перестаёт блокироваться скриптом (повторю: при попытке открыть txt файл левым кликом). Так что в этих функциях что-то не в порядке.
Запускаются скрипты (оба) 6 секунд (вижу песочные часы, если мышь за хвост подёргать).

26 (изменено: Александр_, 2011-09-30 22:08:29)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

teadrinker пишет:

AHK не поддерживает многопоточность.

Это не приговор. AHK действительно некорректно выполняет код в новых потоках, но нам ведь и не нужно весь код в нём выполнять. Мы можем сделать функцию-трамплин, которая дождётся сигнала и отправит сообщение окну. Пример довольно массивный, но должен быть понятен, т.к. комментариев там больше чем кода :

DetectHiddenWindows, on ; чтоб найти скрытое окно AHK
dir := "C:\Users\Александр\Desktop\test\cur" ; директория для мониторинга
FILE_NOTIFY_CHANGE_FILE_NAME :=  1  ; мониторинг изменений имён файлов(функция FindFirstChangeNotification)
INFINITE                     := -1  ; неограниченное время ожидания(функция WaitForSingleObject)
WM_USER                      := 0x400 ; начало диапазона пользовательских сообщений
Unicode                      := A_IsUnicode ? "W" : "A"

FindFirstChangeNotification(PathName, WatchSubtree, NotifyFilter)
; функция начинает отслеживать изменения в файловой системе
; PathName- имя директории
; WatchSubtree- мониторить ли поддиректории?(0- нет, любое другое значение- да)
; NotifyFilter- набор флагов, указывающих какие изменения нужно отслеживать
{
   global Unicode
   return DllCall("FindFirstChangeNotification" . Unicode, "str", PathName, "int", WatchSubtree, "int", NotifyFilter)
}

FindNextChangeNotification(hChangeHandle)
; функция продолжает мониторинг, начатый FindFirstChangeNotification
; hChangeHandle- хэндл, возвращённый функцией FindFirstChangeNotification
{
   return DllCall("FindNextChangeNotification", "int", hChangeHandle)
}

FindCloseChangeNotification(hChangeHandle)
; функция заканчивает мониторинг, начатый FindFirstChangeNotification
; hChangeHandle- хэндл, возвращённый функцией FindFirstChangeNotification
{
   return DllCall("FindCloseChangeNotification", "int", hChangeHandle)
}

CreateThread(ThreadAttributes, StackSize, StartAddress, Parameter, CreationFlags, ThreadId)
; функция создаёт поток
; ThreadAttributes- атрибуты безопасности, почти всегда 0
; StackSize- начальный размер стека, обычно тоже 0
; StartAddress- адрес функции, которую будет исполнять поток
; Parameter- параметр, который получит функция StartAddress
; CreationFlags- флаги создания, обычно 0, иногда 4(тогда поток создаётся, но не начинает выполняться)
; ThreadId- ссылка на переменную, в которую будет помещён идентификатор потока
{
   return DllCall("CreateThread","int",ThreadAttributes,"int",StackSize,"int",StartAddress,"int",Parameter,"int",CreationFlags,"int",ThreadId)
}

CloseHandle(Handle)
; функция закрывает хэндл Handle
{
   return DllCall("CloseHandle","int",Handle)
}

TerminateThread(hThread, ExitCode)
; функция убивает поток по дескриптору hThread
; ЭТУ ФУНКЦИЮ НЕЛЬЗЯ ИСПОЛЬЗОВАТЬ В ОБЫЧНЫХ ПРИЛОЖЕНИЯХ
{
   return DllCall("TerminateThread", "int", hThread, "int", ExitCode)
}

IfUnicodeThenUnicodeToAnsi(ByRef wStr, ByRef str)
; функция конвертирует Unicode-строку в Ascii в unicode-скриптах
; в ascii-скриптах ничего не делает
{
   if (!A_IsUnicode)
      return wStr
   Size := StrLen(wStr)*2
   VarSetCapacity(str, Size)
   DllCall("WideCharToMultiByte", "Uint", 0, "Uint", 0, "Uint", &wStr, "int",  -1, "str",  str, "int",  Size, "Uint", 0, "Uint", 0)
   return str
}

VarSetCapacity(hThread, 4)
;дескриптор потока

VarSetCapacity(hChangeHandle, 4)
;хэндл из FindFirstChangeNotification

VarSetCapacity(hWnd, 4)
WinGet, hWnd, ID, ahk_class AutoHotkey
;хэндл окна

VarSetCapacity(PostMessageAddr, 4)
NumPut(DllCall("GetProcAddress","int",DllCall("GetModuleHandle" . Unicode,"str","user32.dll"),"str",IfUnicodeThenUnicodeToAnsi("PostMessage" . Unicode, tempstr)),PostMessageAddr,0)
; адрес PostMessage

VarSetCapacity(WaitForSingleObjectAddr, 4)
NumPut(DllCall("GetProcAddress","int",DllCall("GetModuleHandle" . Unicode,"str","Kernel32.dll"),"str",IfUnicodeThenUnicodeToAnsi("WaitForSingleObject",tempstr)),WaitForSingleObjectAddr,0)
; адрес WaitForSingleObject

; Функция потока
; Ожидает возврата из WaitForSingleObject и отправляет сообщение окну
; lParam содержит возвращаемое WaitForSingleObject значение
VarSetCapacity(springboard, 38)
NumPut(0x68, springboard, 0, "Char")
NumPut(INFINITE, springboard, 1)
;push -1
NumPut(0x68, springboard, 5, "Char")
;NumPut(hChangeHandle, springboard, 6)
;push hChangeHandle
NumPut(0x15ff, springboard, 10, "Short")
NumPut(&WaitForSingleObjectAddr, springboard, 12)
;call WaitForsingleObject
NumPut(0x50, springboard, 16, "Char")
;push EAX
NumPut(0x6A, springboard, 17, "Char")
NumPut(0x00, springboard, 18, "Char")
;push 0
NumPut(0x68, springboard, 19, "Char")
NumPut(WM_USER, springboard, 20)
;push 0x400
NumPut(0x68, springboard, 24, "Char")
NumPut(hWnd, springboard, 25)
;push hWnd
NumPut(0x15ff, springboard, 29, "Short")
NumPut(&PostMessageAddr, springboard, 31)
;Call PostMessage
NumPut(0xC2, springboard, 35,"char")
NumPut(4, springboard, 36,"short")
;ret 4

ThreadFunc(wParam, lParam)
{
   global hChangeHandle, springboard, hThread
   CloseHandle(hThread)
   if lParam
   {
      ;случился какой-то косяк
      return 0
   }
   MsgBox 4, , Был создан, удалён или переименован какой-то файл`r`nВы хотите продолжить мониторинг?
   IfMsgBox  No
      FindCloseChangeNotification(hChangeHandle)
   else
   {
      FindNextChangeNotification(hChangeHandle)
	  hThread:=CreateThread(0, 64*1024, &springboard, 0, 0, 0)
   }
   return 0
}

;будем обрабатывать сообщение WM_USER, функцией ThreadFunc
OnMessage(WM_USER,"ThreadFunc")

hChangeHandle:=FindFirstChangeNotification( dir, 0, FILE_NOTIFY_CHANGE_FILE_NAME)
NumPut(hChangeHandle, springboard, 6)
hThread:=CreateThread(0, 64*1024, &springboard, 0, 0, 0)
return

F12::
   ; Так делать не надо, этот код исключительно для удобства тестирования
   TerminateThread(hThread, -1)
   CloseHandle(hThread)
   FindCloseChangeNotification(hChangeHandle)
return

Прошу обратить внимание на 2 момента, касающихся потоков:
1) Нужно всегда закрывать дескрипторы потоков- если код потока завершился, а дескриптор остался, то поток "жив".
2) Нельзя использовать функцию TerminateThread в приложениях.

mih пишет:

Выяснилось, что дело не конкретном редакторе, дело в наличии ключа HKEY_LOCAL_MACHINE\SOFTWARE\Classes\EditPlus 2.txt\shell\open\ddeexec  ("EditPlus 2.txt" — это т. наз. "тип", к которому у меня относится *.txt ). Если убираю эту ветку (просто переименовываю "ddeexec" в "ddeexec1") — Проводник перестаёт блокироваться скриптом (повторю: при попытке открыть txt файл левым кликом). Так что в этих функциях что-то не в порядке.
Запускаются скрипты (оба) 6 секунд (вижу песочные часы, если мышь за хвост подёргать).

Это в вашем редакторе что-то не в порядке. У вас текстовые файлы имеют "псевдоним" "EditPlus 2.txt"(см. значение по умолчанию раздела ".txt") и именно этот раздел задаёт поведение проводника касательно текстовых файлов. Поставьте другой текстовый редактор- он заменит этот "псевдоним" на свой и проблема исчезнет.

27

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Стоп, стоп. Здесь явная ошибка:

DllCall("FindFirstChangeNotification". Unicode, "str", PathName, "int", WatchSubtree, "int", NotifyFilter)

перед знаком конкатеции нужен пробел, иначе выдаёт ошибку.
Не считая этого пример хороший, по-моему достоин Коллекции.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

28

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

teadrinker пишет:

Стоп, стоп. Здесь явная ошибка:

DllCall("FindFirstChangeNotification". Unicode, "str", PathName, "int", WatchSubtree, "int", NotifyFilter)

перед знаком конкатеции нужен пробел, иначе выдаёт ошибку.

У меня корректно выполняется, никаких ошибок не выдаёт.

29

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Видимо, версия AHK устаревшая.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

30

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Александр_, тебе присвоен статус разработчика. Теперь ты имеешь право постить в Коллекцию. Прошу добавить туда последний код с исправленной конкатенцией.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

31

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

teadrinker пишет:

Видимо, версия AHK устаревшая.

Точно, у меня 1.0.48.05 была. Жаль, что новые версии понимают только одну кодировку, проверка выполнения в другой становится проблематичной.
Исправил код в посте+нашёл ещё один косяк- функция GetProcAddress принимает ASCII строку, поэтому в юникоде нужны дополнительные телодвижения. Код исправлен путём добавления функции с говорящим названием "IfUnicodeThenUnicodeToAnsi" . Код проверен только на Unicode-версии интерпретатора.

teadrinker пишет:

Прошу добавить туда последний код с исправленной конкатенцией.

А там уже есть похожая тема. Только там вставлен большой кусок кода для ускорения вычислений, а тут всего 38 байт для выполнения блокирующей поток функции.

32

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Александр_ пишет:

Жаль, что новые версии понимают только одну кодировку, проверка выполнения в другой становится проблематичной.

Я для этого пользуюсь другим расширением скрипта, поставленным в соответствие альтернативному AutoHotkey.exe.

Думаю, ничего страшного не случится, если темы в Коллекции будут в чём-то пересекаться. Это только поможет усвоению материала, который не из тривиальных.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

33 (изменено: mih, 2011-09-30 18:21:14)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Для полноты картины сообщу, что последний скрипт работает и с моим EditPlus, и тормозов при запуске (6 сек) — нет. Те же два предыдущих скрипта — блокировали Проводник и при открытии, например, *.htm в Опере, так что редактор невиновен.

34

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

А вот у меня последний скрипт в ANSI-версии вызывает аварийное завершение. В Unicode работает.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

35

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

teadrinker пишет:

А вот у меня последний скрипт в ANSI-версии вызывает аварийное завершение. В Unicode работает.

Исправил, синтаксис условных операторов немного поменялся в новой версии

mih пишет:

Для полноты картины сообщу, что последний скрипт работает и с моим EditPlus, и тормозов при запуске (6 сек) — нет. Те же два предыдущих скрипта — блокировали Проводник и при открытии, например, *.htm в Опере, так что редактор невиновен.

Эти 3 скрипта делают одно и тоже, просто немного разным способом. И они ну никак не повлияют на "чистый" проводник(я даже проверил на шести виртуалках XP SP0/1/2/3 и seven SP0/1). Пропажа тормозов может объясняться сменой набора файлов в папке, при одинаковых условиях тестирования все 3 скрипта должны оказывать одинаковый эффект. Когда буду "убивать" очередную систему, поставлю туда EditPlus. После этого можно будет вынести ему приговор.

36 (изменено: mih, 2011-10-03 19:11:07)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Насчёт Проводника и редактора — заметьте, я и про Оперу (9.64) написал. А теперь напишу и про ToтalCommander (7.56a) вместо Проводника — заедает точно так же, проверил. То есть теперь — "TC+Опера" вместо "Проводник+EditPlus" — что общего у этих пар? Скрипт, и наличие ключей "ddeexec" в реестре для txt и htm. Проверять так: Опера (редактор) должна быть запущена, запускается скрипт, создается новый файл htm (txt), дается "добро" скрипту на продолжение — и кликается по этому новому файлу — TC (Проводник) виснут; скрипт убивается — файл тут же сам собой открывается. Отключение dde проблему снимает. А тормоза при запуске старых скриптов совершенно не зависят от содержимого папки, тоже ещё раз проверил. Это, конечно, "в прошлом", и победителя не судят, но иногда так вскрываются всякие незамеченные детали.

Александр_, уточните пожалуйста: "синтаксис условных операторов немного поменялся в новой версии" — что конкретно изменили; отмечено ли это в Справке? Не сохранил старую версию скрипта, не могу сравнить. А то действительно, — завершался аварийно ansi вариант, и ничего не диагностировалось, странно.

37

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

mih пишет:

Насчёт Проводника и редактора — заметьте, я и про Оперу (9.64) написал. А теперь напишу и про ToтalCommander (7.56a) вместо Проводника — заедает точно так же, проверил. То есть теперь — "TC+Опера" вместо "Проводник+EditPlus" — что общего у этих пар? Скрипт, и наличие ключей "ddeexec" в реестре для txt и htm. Проверять так: Опера (редактор) должна быть запущена, запускается скрипт, создается новый файл htm (txt), дается "добро" скрипту на продолжение — и кликается по этому новому файлу — TC (Проводник) виснут; скрипт убивается — файл тут же сам собой открывается. Отключение dde проблему снимает. А тормоза при запуске старых скриптов совершенно не зависят от содержимого папки, тоже ещё раз проверил. Это, конечно, "в прошлом", и победителя не судят, но иногда так вскрываются всякие незамеченные детали.

Я протестировал скрипты в системе с установленным EditPlus'ом и никаких тормозов не было, значит он не виновен... Нужно узнать на кой чёрт этот параметр реестра нужен, а пока вопрос остаётся открытым .

mih пишет:

Александр_, уточните пожалуйста: "синтаксис условных операторов немного поменялся в новой версии" — что конкретно изменили; отмечено ли это в Справке? Не сохранил старую версию скрипта, не могу сравнить. А то действительно, — завершался аварийно ansi вариант, и ничего не диагностировалось, странно.

Там была строка:

if !A_IsUnicode return wStr

В моей, устаревшей, версии AHK это прокатывало, а в новой нет.

38 (изменено: mih, 2011-10-04 19:19:41)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Ясно. Как синтакс. ошибку AHK это не видит, просто всегда проходит вниз.
Посмотрел Румянцев "Работа с файлами", и понял так, что этот механизм не даёт нам информации о конкретных изменениях: именах файлов и папок. Это так?

Существует уникальная, очень полезная в быту "Everything" ("Everything Search Engine"), развитие её под вопросом. Все изменения в файловой системе она регистрирует моментально. Интересно, какими механизмами она пользуется?

И я рискую навлечь громы-молнии, но: поставьте над (или под) "f12::" ещё, например, "f11::" — и посмотрите в монитор при выполнении Вашего скрипта: что происходит по f12, а что по f11? У меня что-то совсем странное — f12 выводит совсем не туда (даже боюсь писать) — и в результате попадает на "случился какой-то косяк" и return 0; и слежение продолжается. А по f11 — нормально. Уже перезагружался, и всё поотключал. Не верите мне? Я не соврал! Сам такого никогда не видал.

39

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

mih, есть ещё подписка на события WMI: VBScript: слежение за созданием файлов в указанном каталоге.

Вот обсуждения:
VBScript: Слежение за изменениями файлов в директории?
VBS: условие выполнения - изменение файла в каталоге ...
VBScript: Помогите модифицировать скрипт слежения за созданием файлов
VBS: архивирование файла после его создания

40

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

mih пишет:

Ясно. Как синтакс. ошибку AHK это не видит, просто всегда проходит вниз.
Посмотрел Румянцев "Работа с файлами", и понял так, что этот механизм не даёт нам информации о конкретных изменениях: именах файлов и папок. Это так?

Да. В Windows XP появилась функция ReadDirectoryChangesW, которой и нужно пользоваться для получения подробных сведений об изменениях. У Румянцева она не описана, т.к. второе издание вышло в 2000 году, а XP вышла только в октябре 2001. Я нигде не встречал подробного описания этой функции  на русском языке. Чуть позже напишу пример её использования.

mih пишет:

Существует уникальная, очень полезная в быту "Everything" ("Everything Search Engine"), развитие её под вопросом. Все изменения в файловой системе она регистрирует моментально. Интересно, какими механизмами она пользуется?

ReadDirectoryChangesW и FindFirstChangeNotification/FindNextChangeNotification тоже моментально реагируют на изменения в файловой системе. Про механизм уведомлений в файловой системе NTFS на русском языке можно почитать в книге Марка Руссиновича "Внутреннее устройство Microsoft Windows", четвёртое издание. Там подробно описана структура файловой системы, очень полезная информация. Например дополнительные файловые потоки очень модно использовать для скрытия файлов(к примеру запись в поток к файлу подкачки, после перезагрузки эти данные нельзя будет не только прочитать, но и установить их существование).

mih пишет:

И я рискую навлечь громы-молнии, но: поставьте над (или под) "f12::" ещё, например, "f11::" — и посмотрите в монитор при выполнении Вашего скрипта: что происходит по f12, а что по f11? У меня что-то совсем странное — f12 выводит совсем не туда (даже боюсь писать) — и в результате попадает на "случился какой-то косяк" и return 0; и слежение продолжается. А по f11 — нормально. Уже перезагружался, и всё поотключал. Не верите мне? Я не соврал! Сам такого никогда не видал.

Не знаю в чём причина, но я в комментариях предупреждал, что нельзя убивать потоки функцией TerminateThread. Нужно использовать WaitForMultipleObjects и передавать ей дополнительный объект, состоянием которого будет управлять скрипт.

41 (изменено: mih, 2011-10-05 18:24:36)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Спасибо — и Alexii, и Александр_.

Александр_, меня очень заинтриговало это странное невыполнение по F12. Вы мне скажите —  у Вас это повторяется? Просто поставьте ловушку после F12, и посмотрите — попадёте вы на нее по F12? Я не попадаю, но очевидно, что скрипт F12 ПЕРЕХВАТЫВАЕТ, но затем делает ерунду — это видно в отладчике!  (точно так же будут работать F12 с модификаторами)

А если добавить на этот участок кода, например, F10 — то эта F10 нормально ведёт на ловушку, и далее слежение отключается.

У Вас так?

42

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

mih пишет:

Спасибо — и Alexii, и Александр_.

Александр_, меня очень заинтриговало это странное невыполнение по F12. Я только не понял, у Вас это повторяется? Вы просто поставьте ловушку после F12 (какой-нибудь msgbox), и посмотрите — попадёте вы на нее по F12? Я не попадаю, но очевидно, что скрипт F12 ПЕРЕХВАТЫВАЕТ и делает ерунду — это видно в отладчике!  (также будут работать F12 с модификаторами)

А если сменить (добавить) на этот участок кода, например, F10 — нормально попадаю на ловушку, и далее слежение отключается. Ответьте — у Вас так?

Да, именно так, при нажатии F12 происходит что-то непонятное(обработчик управления не получает, а WaitForSingleObject возвращает 88(в документации этот случай не описан)), в то время как остальные хоткеи нормально работают.

43 (изменено: mih, 2011-10-09 17:02:29)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Вышеупомянутое 4 изд.

44 (изменено: Александр_, 2011-10-10 21:20:13)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Александр_ пишет:

Чуть позже напишу пример её использования.

Ну вот как-то так:

DetectHiddenWindows, on ; чтоб найти скрытое окно AHK
dir                := "C:\Users\Александр\Desktop\test\cur" ; директория для мониторинга
WM_USER            := 0x400 ; начало диапазона пользовательских сообщений
WM_DIRECTORYCHANGE := WM_USER + 1
Unicode            := A_IsUnicode ? "W" : "A"
log                := Unicode . "`n"
 
; что могут делать с файлом другие программы
FILE_SHARE_READ  := 1
FILE_SHARE_WRITE := 2
FILE_SHARE_DELETE := 4 ; в сочитании с флагом FILE_FLAG_BACKUP_SEMANTICS приведёт к блокировке доступа к папке
 
; CreateFile, DesiredAccess
GENERIC_READ     := 0x80000000
GENERIC_WRITE    := 0x40000000
; дополнительные атрибуты доступа
; тут необходимо указать FILE_LIST_DIRECTORY
FILE_ADD_FILE             := 0x0002
FILE_ADD_SUBDIRECTORY     := 0x0004
FILE_APPEND_DATA          := 0x0004
FILE_CREATE_PIPE_INSTANCE := 0x0004
FILE_DELETE_CHILD         := 0x0040
FILE_EXECUTE              := 0x0020
FILE_LIST_DIRECTORY       := 0x0001 ;право просматривать содержание директории
FILE_READ_ATTRIBUTES      := 0x0080
FILE_READ_DATA            := 0x0001
FILE_READ_EA              := 0x0008
FILE_TRAVERSE             := 0x0020
FILE_WRITE_ATTRIBUTES     := 0x0100
FILE_WRITE_DATA           := 0x0002
FILE_WRITE_EA             := 0x0010
 
; события получаемые в структуре FILE_NOTIFY_INFORMATION
FILE_ACTION_ADDED            := 0x00000001
FILE_ACTION_REMOVED          := 0x00000002
FILE_ACTION_MODIFIED         := 0x00000003 ; в данном примере не используется, информирует об изменении файла 
FILE_ACTION_RENAMED_OLD_NAME := 0x00000004
FILE_ACTION_RENAMED_NEW_NAME := 0x00000005 
 
; способы открытия файлов функцией CreateFile
CREATE_NEW        := 1
CREATE_ALWAYS     := 2
OPEN_EXISTING     := 3
OPEN_ALWAYS       := 4
TRUNCATE_EXISTING := 5
 
; флаги и атрибуты, используемые при открытии файлов функцией CreateFile
; здесь используется только FILE_FLAG_BACKUP_SEMANTICS
FILE_ATTRIBUTE_READONLY   := 0x00000001
FILE_ATTRIBUTE_HIDDEN     := 0x00000002
FILE_ATTRIBUTE_SYSTEM     := 0x00000004
FILE_ATTRIBUTE_DIRECTORY  := 0x00000010
FILE_ATTRIBUTE_ARCHIVE    := 0x00000020
FILE_ATTRIBUTE_NORMAL     := 0x00000080
FILE_ATTRIBUTE_TEMPORARY  := 0x00000100
FILE_ATTRIBUTE_COMPRESSED := 0x00000800
FILE_FLAG_POSIX_SEMANTICS  := 0x01000000
FILE_FLAG_BACKUP_SEMANTICS := 0x02000000
FILE_FLAG_DELETE_ON_CLOSE  := 0x04000000
FILE_FLAG_SEQUENTIAL_SCAN  := 0x08000000
FILE_FLAG_RANDOM_ACCESS    := 0x10000000
FILE_FLAG_NO_BUFFERING     := 0x20000000
FILE_FLAG_OVERLAPPED       := 0x40000000
FILE_FLAG_WRITE_THROUGH    := 0x80000000
 
; какие события отслеживать
; в примере только изменения имён файлов(FILE_NOTIFY_CHANGE_FILE_NAME)
FILE_NOTIFY_CHANGE_FILE_NAME   := 0x00000001
FILE_NOTIFY_CHANGE_DIR_NAME    := 0x00000002
FILE_NOTIFY_CHANGE_ATTRIBUTES  := 0x00000004
FILE_NOTIFY_CHANGE_SIZE        := 0x00000008
FILE_NOTIFY_CHANGE_LAST_WRITE  := 0x00000010
FILE_NOTIFY_CHANGE_LAST_ACCESS := 0x00000020
FILE_NOTIFY_CHANGE_CREATION    := 0x00000040
FILE_NOTIFY_CHANGE_SECURITY    := 0x00000100 
 
CreateFile(FileName, DesiredAccess, ShareMode, SecurityAttributes, CreationDisposition, FlagsAndAttributes, hTemplateFile)
; FileName- путь к файлу
; DesiredAccess- требуемый доступ(GENERIC_*)
; ShareMode- права на совместный доступ(FILE_SHARE_)
; SecurityAttributes- атрибуты защиты(обычно нуль)
; CreationDisposition- действия над файлом
; FlagsAndAttributes- атрибуты и флаги файла(FILE_ATTRIBUTE_* и FILE_FLAG_*, обычно FILE_ATTRIBUTE_NORMAL)
; hTemplateFile- файл, от которого наследуется FlagsAndAttributes(обычно 0)
{
   global Unicode
   return DllCall("CreateFile" . Unicode, "str", FileName, "int", DesiredAccess, "int", ShareMode, "int", SecurityAttributes, "int", CreationDisposition, "int", FlagsAndAttributes, "int", hTemplateFile)
}
 
CreateThread(ThreadAttributes, StackSize, StartAddress, Parameter, CreationFlags, ThreadId)
; функция создаёт поток
; ThreadAttributes- атрибуты безопасности, почти всегда 0
; StackSize- начальный размер стека, обычно тоже 0
; StartAddress- адрес функции, которую будет исполнять поток
; Parameter- параметр, который получит функция StartAddress
; CreationFlags- флаги создания, обычно 0, иногда 4(тогда поток создаётся, но не начинает выполняться)
; ThreadId- ссылка на переменную, в которую будет помещён идентификатор потока
{
   return DllCall("CreateThread","int",ThreadAttributes,"int",StackSize,"int",StartAddress,"int",Parameter,"int",CreationFlags,"int",ThreadId)
}
 
CloseHandle(Handle)
; функция закрывает хэндл Handle
{
   return DllCall("CloseHandle","int",Handle)
}
 
WriteProcessMemory(hProcess, BaseAddress, Buffer, Size, NumberOfBytesWritten)
; функция записывает Size байт с адреса Buffer в процесс hProcess по адресу BaseAddress
; в NumberOfBytesWritten помещает число записанный байтов
{
   return DllCall("WriteProcessMemory", "int", hProcess, "int", BaseAddress, "int", Buffer, "int", Size, "int", NumberOfBytesWritten)
}
 
IfUnicodeThenUnicodeToAnsi(ByRef wStr, ByRef str)
{
   if (!A_IsUnicode)
      return wStr
   Size := StrLen(wStr)+1
   VarSetCapacity(str, Size)
   DllCall("WideCharToMultiByte", "Uint", 0, "Uint", 0, "Uint", &wStr, "int",  -1, "str",  str, "int",  Size, "Uint", 0, "Uint", 0)
   return str
}
 
IfAnsiThenUnicodeToAnsi(ByRef wStr, ByRef str, len)
{
   if A_IsUnicode
      return wStr
   len := len/2+1
   VarSetCapacity(str, len)
   DllCall("WideCharToMultiByte", "Uint", 0, "Uint", 0, "Uint", &wStr, "int",  -1, "str",  str, "int",  len, "Uint", 0, "Uint", 0)
   return str
}
 
VarSetCapacity(hThread, 4)
;дескриптор потока
 
VarSetCapacity(hDirectory, 4)
;хэндл из FindFirstChangeNotification
 
VarSetCapacity(hWnd, 4)
PID := DllCall("GetCurrentProcessId")
WinGet, hWnd, ID, ahk_pid %PID% ahk_class AutoHotkey
;хэндл окна
 
VarSetCapacity(PostMessageAddr, 4)
NumPut(DllCall("GetProcAddress","int",DllCall("GetModuleHandle" . Unicode,"str","user32.dll"),"str",IfUnicodeThenUnicodeToAnsi("PostMessage" . Unicode, tempstr)),PostMessageAddr,0)
; адрес PostMessage
log := log . "PostMessageAddr, " . %errorlevel% . ", " . %a_lasterror% . "`n"
 
VarSetCapacity(ReadDirectoryChangesWAddr, 4)
NumPut(DllCall("GetProcAddress","int",DllCall("GetModuleHandle" . Unicode,"str","Kernel32.dll"),"str",IfUnicodeThenUnicodeToAnsi("ReadDirectoryChangesW",tempstr)),ReadDirectoryChangesWAddr,0)
; адрес ReadDirectoryChangesW
log := log . "ReadDirectoryChangesWAddr, " . %errorlevel% . ", " . %a_lasterror% . "`n"
 
VarSetCapacity(BytesReturned, 4)
VarSetCapacity(outBuffer, 0x400)
 
; Функция потока
; Ожидает возврата из ReadDirectoryChangesW и отправляет сообщение окну
; lParam содержит возвращаемое ReadDirectoryChangesW значение
VarSetCapacity(springboard, 100)
NumPut(0x68, springboard, 0)
NumPut(0x00, springboard, 1)
;push 0
NumPut(0x68, springboard, 5)
NumPut(0x00, springboard, 6)
;push 0
NumPut(0x68, springboard, 10)
NumPut(&BytesReturned, springboard, 11)
;push ADDR BytesReturned
NumPut(0x68, springboard, 15)
NumPut(FILE_NOTIFY_CHANGE_FILE_NAME, springboard, 16)
;push FILE_NOTIFY_CHANGE_FILE_NAME
NumPut(0x68, springboard, 20)
NumPut(0x00, springboard, 21)
;push 0
NumPut(0x68, springboard, 25)
NumPut(0x400, springboard, 26)
;push 0x400
NumPut(0x68, springboard, 30)
NumPut(&outBuffer, springboard, 31)
;push ADDR outBuffer
NumPut(0x68, springboard, 35, "Char")
;NumPut(hDirectory, springboard, 36)
;push hDirectory
NumPut(0x15ff, springboard, 40, "Short")
NumPut(&ReadDirectoryChangesWAddr, springboard, 42)
;call ReadDirectoryChangesW
NumPut(0x50, springboard, 46, "Char")
;push EAX
NumPut(0x6A, springboard, 47, "Char")
NumPut(0x00, springboard, 48, "Char")
;push 0
NumPut(0x68, springboard, 49, "Char")
NumPut(WM_DIRECTORYCHANGE, springboard, 50)
;push 0x400
NumPut(0x68, springboard, 54, "Char")
NumPut(hWnd, springboard, 55)
;push hWnd
NumPut(0x15ff, springboard, 59, "Short")
NumPut(&PostMessageAddr, springboard, 61)
;Call PostMessage
NumPut(0xC2, springboard, 65,"char")
NumPut(4, springboard, 66,"short")
;ret 4
 
ThreadFunc(wParam, lParam)
{
   global
   log := log . "springboard, " . %lParam% . ", " . %a_lasterror% . "`n"
   if (!lParam)
   {
      ;случился какой-то косяк
      msgbox "exit", %lParam%, %A_lasterror%
      return 0
   }
   next := 0
   loop
   {
   type := NumGet(outBuffer,4+next)
   if (type == FILE_ACTION_ADDED)
      msg := "Создан файл "
   else if (type = FILE_ACTION_REMOVED)
      msg := "Удалён файл "
   else if (type = FILE_ACTION_RENAMED_OLD_NAME)
      msg := "Файл переименован из "
   else if (type = FILE_ACTION_RENAMED_NEW_NAME)
      msg := "Файл переименован в "
   else 
      msg := type . " Хрен его знает, что произошло с файлом "
   NameLen := NumGet(outBuffer,8+next)
   VarSetCapacity(fn, NameLen+2)
   WriteProcessMemory(DllCall("GetCurrentProcess"), &fn, &outBuffer+12+next, NameLen, &BytesReturned)
   log := log . "WriteProcessMemory, " . %errorlevel% . ", " . %a_lasterror% . "`n"
   NumPut(0x0000, fn, NameLen ,"short")
   next := NumGet(outBuffer, next)
   LV_Add("", msg, IfAnsiThenUnicodeToAnsi(fn, str, NameLen) )
   if (!next)
      break
   }
   hThread:=CreateThread(0, 64*1024, &springboard, 0, 0, 0)
   log := log . "CreateThread, " . %hThread% . ", " . %errorlevel% . ", " . %a_lasterror% . "`n"
   CloseHandle(hThread)
   return 0
}
 
;будем обрабатывать сообщение WM_DIRECTORYCHANGE, функцией ThreadFunc
OnMessage(WM_DIRECTORYCHANGE,"ThreadFunc")
 
hDirectory := CreateFile(dir, FILE_LIST_DIRECTORY, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)
if (hDirectory=-1)
{
   msgbox CreateFile error, %a_lasterror%
   ExitApp
}
NumPut(hDirectory, springboard, 36)
Gui Add, ListView, r20 w600 NoSort, Действие|Имя файла
LV_ModifyCol(1, 200)
LV_ModifyCol(2, 396)
Gui Show
hThread:=CreateThread(0, 64*1024, &springboard, 0, 0, 0)
log := log . "CreateThread, " . %hThread% . ", " . %errorlevel% . ", " . %a_lasterror% . "`n"
CloseHandle(hThread)
return 
 
GuiClose:
   msgbox %log%
   clipboard = %log%
   ExitApp
return

В данном примере показан синхронный вызов функции в отдельном потоке, но предусмотрен так же и асинхронный вызов(для него потребовалось бы сильнее модифицировать код предыдущего примера). И желательно предусмотреть ситуацию, когда выделенного буфера(2^10 байт) не хватит. Функция довольно сложная и мне будет проще отвечать на возникающие вопросы, чем пытаться все варианты описать в коде.

mih пишет:

Вышеупомянутое 4 изд.

?

45 (изменено: mih, 2011-10-10 06:06:31)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

"?" — см. "."

Пока не работает, таблица пустая; XP SP2.

46

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

mih пишет:

Пока не работает, таблица пустая; XP SP2.

Проверил на 32-битных системах Win7 SP1 и WinXP SP3, интерпретаторы брал по ссылке teadrinker'а несколькими постами ранее. Проверял и ansi и unicode версии. Нужно добавить отлов ошибок и посмотреть в каком месте косяк.

47

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Александр_, прости, квалификации не хватает разобраться. Могу трассировку прислать, там просто ничего не происходит при создании/удалении файла (сразу и навсегда останавливается на return, который четвёртая от конца строка). Интерпретатор последний, пробовал ansi и unicode.

48

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

mih пишет:

Могу трассировку прислать, там просто ничего не происходит при создании/удалении файла (сразу и навсегда останавливается на return, который четвёртая от конца строка). Интерпретатор последний, пробовал ansi и unicode.

Добавил логирование после основных вызовов в пост с кодом. При закрытии окна лог выводится в окно MessageBox, а потом копируется в буфер обмена. Продемонстрируйте этот лог пожалуйста.

49

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Александр_ пишет:
Александр_ пишет:

Чуть позже напишу пример её использования.

Ну вот как-то так:

Интересно, правда, ещё не было времени разобраться, как это работает. Может, имеет смысл дополнить пост в Коллекции?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

50 (изменено: mih, 2011-10-11 15:27:41)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Я виноват, —  в первый раз у меня не было "DetectHiddenWindows, on" — видимо, потерял при копировании.

Работает, отлично работает, не следит только за субдиректориями (и изменением файла (FILE_ACTION_MODIFIED)).

Как работает, что там делается — не понимаю.

51 (изменено: Александр_, 2011-10-13 03:18:15)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

teadrinker пишет:
Александр_ пишет:
Александр_ пишет:

Чуть позже напишу пример её использования.

Ну вот как-то так:

Интересно, правда, ещё не было времени разобраться, как это работает. Может, имеет смысл дополнить пост в Коллекции?

Да, но позже. Во-первых использование асинхронного вызова было бы тут значительно рациональнее, мне просто сильно код модифицировать не хотелось(а вариантов асинхронного вызова у этой функции два- через функцию обратного вызова и через "событие"(event)). А во-вторых непонятно куда эта тема нас заведёт, может и до настройки журнала изменений в NTFS дойдём. А работает это как и FindFirstChangeNotification, только возвращает ещё и структуру с именами изменённых файлов и типом изменений(типов всего 5).

mih пишет:

Работает, отлично работает, не следит только за субдиректориями (и изменением файла (FILE_ACTION_MODIFIED)).

За слежение за субдиректориями отвечает четвёртый параметр:

NumPut(0x68, springboard, 20)
NumPut(0x00, springboard, 21)
;push 0

Если указать там единицу, то будет следить:

NumPut(0x68, springboard, 20)
NumPut(0x01, springboard, 21)
;push 1

Ну а пятый параметр указывает критерии слежения:

NumPut(0x68, springboard, 15)
NumPut(FILE_NOTIFY_CHANGE_FILE_NAME, springboard, 16)
;push FILE_NOTIFY_CHANGE_FILE_NAME

А ранее был перечень возможных значений:

; какие события отслеживать
; в примере только изменения имён файлов(FILE_NOTIFY_CHANGE_FILE_NAME)
FILE_NOTIFY_CHANGE_FILE_NAME   := 0x00000001
FILE_NOTIFY_CHANGE_DIR_NAME    := 0x00000002
FILE_NOTIFY_CHANGE_ATTRIBUTES  := 0x00000004
FILE_NOTIFY_CHANGE_SIZE        := 0x00000008
FILE_NOTIFY_CHANGE_LAST_WRITE  := 0x00000010
FILE_NOTIFY_CHANGE_LAST_ACCESS := 0x00000020
FILE_NOTIFY_CHANGE_CREATION    := 0x00000040
FILE_NOTIFY_CHANGE_SECURITY    := 0x00000100

Например для отслеживания изменений в именах файлов и папок, а так же их атрибутах нужно указать:

NumPut(0x68, springboard, 15)
NumPut(FILE_NOTIFY_CHANGE_FILE_NAM|FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES, springboard, 16)

В ближайшее время перепишу, чтоб ReadDirectoryChangesW не в машинном коде вызывалась .

52 (изменено: mih, 2011-10-13 15:57:30)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Я вижу, что скрипт может реагировать на изменения размера, трех типов времени и пр., и на всё это возвращает только "3" (т.е. "FILE_ACTION_MODIFIED") — из пяти возможных (почему-то чаще всего дважды). Как же различать эти события?

Александр_, объясните — как это работает по сути? Что происходит при запуске скрипта и ожидании, а затем — что происходит (например) при создании файла? Приходит прерывание скрипту?

И какой механизм в предпоследнем скрипте ломает AHK так, что по F12:: он делает непонятно что; и, к тому же, отличает F12:: от F11::, хотя они указывают на один фрагмент?

53

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

mih пишет:

Я вижу, что скрипт может реагировать на изменения размера, трех типов времени и пр., и на всё это возвращает только "3" (т.е. "FILE_ACTION_MODIFIED") — из пяти возможных (почему-то чаще всего дважды). Как же различать эти события?

Только непосредственной проверкой, к сожалению более подробную информацию для произвольной файловой системы получить нельзя.

mih пишет:

Александр_, объясните — как это работает по сути? Что происходит при запуске скрипта и ожидании, а затем — что происходит (например) при создании файла? Приходит прерывание скрипту?

Основной поток создаёт дополнительный и продолжает работу(обработку горячих клавиш, например). В это время дополнительный поток ждёт от системы сообщение о каком-нибудь событии. Как только это событие происходит, дополнительный поток сообщает об этом основному и завершает свою работу.
Функцией CreateThread создаётся поток, а в качестве функции обратного вызова указана переменная(область памяти) springboard, которая содержит машинный код. На самом деле ничего сложного в этом нет. Та самая функция выглядит так:

x := ReadDirectoryChangesW(hDirectory, &outBuffer, 0x400, 0, FILE_NOTIFY_CHANGE_FILE_NAME, &BytesReturned, 0, 0)
PostMessage(hWnd, WM_DIRECTORYCHANGE, 0, x)
return

объяснить как этот код был составлен очень сложно, желательно иметь минимальные представления об ассемблере. Если интересно, то я бы порекомендовал знакомится с ним через крэкинг- это не так продуктивно как документация от intel, но зато интереснее (отсюда первых пять статей и всё с этим кодом будет ясно).
Когда вызывается функция ReadDirectoryChangesW, поток, который её вызывал, начинает ждать пока не произойдёт какое-нибудь событие. Как только оно произойдёт, функция ReadDirectoryChangesW вернёт управление пользовательскому коду, а outBuffer будет содержать информацию об изменениях и BytesReturned будет содержать размер этой информации в байтах. У нас в коде после ReadDirectoryChangesW сразу вызывается PostMessage, т.е. окну посылается сообщение. А обработчиком этого сообщения является функция ThreadFunc, которая и анализирует буфер outBuffer, который был заполнен функцией ReadDirectoryChangesW.

mih пишет:

И какой механизм в предпоследнем скрипте ломает AHK так, что по F12:: он делает непонятно что; и, к тому же, отличает F12:: от F11::, хотя они указывают на один фрагмент?

Не знаю .

54 (изменено: mih, 2011-10-14 23:18:50)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

"Даже не знаю что сказать" (из Василия Пупкина).

Про поломку AHK: а что же значит комментарий "так не делать, только для удобства тестирования" —  предчувствие было всё-же? В последем скрипте нет таких нарушений?

Спасибо за разъяснения.

55 (изменено: Александр_, 2011-10-14 23:40:27)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

mih пишет:

Про поломку AHK: а что же значит комментарий "так не делать, только для удобства тестирования" —  предчувствие было всё-же? В последем скрипте нет таких нарушений?

Нет, это не "предчувствие" . Предупреждение касалось использования функции TerminateThread для завершения потоков- при таком завершении ресурсы, используемые тем потоком, не освобождаются, что приводит к нестабильной работе приложения. С появившимся глюком это никак не связано.
Кстати насчёт ReadDirectoryChangesW- оказывается асинхронный вызов тоже является блокирующим, поэтому нет смысла рассматривать этот случай.

56 (изменено: mih, 2011-10-16 19:11:00)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Про "предчувствие" исходил из этого:

Александр_ пишет:
mih пишет:

поставьте над (или под) "f12::" ещё, например, "f11::" — и посмотрите ... — f12 выводит совсем не туда.

Не знаю в чём причина, но я в комментариях предупреждал, что нельзя ...

А что эта часть скрипта к делу отношения не имеет, — это и тогда ясно было, я просто подзабыл.

Но если в коде нет ничего некорректного, — то это первый серьёзный глюк, который я вижу у AHK (L?). Привык, знаете ли, к надёжной работе AHK, а тут — среди других выделяет именно f12, и делает, что хочет. Кстати, в Разработке — именно этот вариант. Наверное, надо заметку там сделать.

57 (изменено: Александр_, 2011-10-22 22:06:59)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

teadrinker пишет:

Может, имеет смысл дополнить пост в Коллекции?

Думаю можно следующий пример добавить:

#include %A_ScriptDir%\windows.ahk

DetectHiddenWindows, on ; чтоб найти скрытое окно AHK
WM_USER                      := 0x400 ; начало диапазона пользовательских сообщений
WM_DIRECTORYCHANGE           := WM_USER + 1

IfAnsiThenUnicodeToAnsi(ByRef wStr, ByRef str, len)
{
   if A_IsUnicode
      return wStr
   len := len/2+1
   VarSetCapacity(str, len)
   DllCall("WideCharToMultiByte", "Uint", 0, "Uint", 0, "Uint", &wStr, "int",  -1, "str",  str, "int",  len, "Uint", 0, "Uint", 0)
   return str
}

VarSetCapacity(hThread, 4)

VarSetCapacity(hDirectory, 4)

VarSetCapacity(hWnd, 4)
PID := GetCurrentProcessId()
WinGet, hWnd, ID, ahk_pid %PID% ahk_class AutoHotkey

VarSetCapacity(PostMessageAddr, 4)
NumPut(GetProcAddress(GetModuleHandle("user32.dll"), "PostMessage" . Unicode),PostMessageAddr,0)

VarSetCapacity(ReadDirectoryChangesWAddr, 4)
NumPut(GetProcAddress(GetModuleHandle("Kernel32.dll"),"ReadDirectoryChangesW"),ReadDirectoryChangesWAddr,0)

VarSetCapacity(BytesReturned, 4)
VarSetCapacity(outBuffer, 0x400)

VarSetCapacity(springboard, 100)
NumPut(0x68, springboard, 0)
NumPut(0x00, springboard, 1)
NumPut(0x68, springboard, 5)
NumPut(0x00, springboard, 6)
NumPut(0x68, springboard, 10)
NumPut(&BytesReturned, springboard, 11)
NumPut(0x68, springboard, 15)
;NumPut(FILE_NOTIFY_CHANGE_FILE_NAME, springboard, 16)
NumPut(0x68, springboard, 20)
;NumPut(0x00, springboard, 21)
NumPut(0x68, springboard, 25)
NumPut(0x400, springboard, 26)
NumPut(0x68, springboard, 30)
NumPut(&outBuffer, springboard, 31)
NumPut(0x68, springboard, 35, "Char")
;NumPut(hDirectory, springboard, 36)
NumPut(0x15ff, springboard, 40, "Short")
NumPut(&ReadDirectoryChangesWAddr, springboard, 42)
NumPut(0x50, springboard, 46, "Char")
NumPut(0x6A, springboard, 47, "Char")
NumPut(0x00, springboard, 48, "Char")
NumPut(0x68, springboard, 49, "Char")
NumPut(WM_DIRECTORYCHANGE, springboard, 50)
NumPut(0x68, springboard, 54, "Char")
NumPut(hWnd, springboard, 55)
NumPut(0x15ff, springboard, 59, "Short")
NumPut(&PostMessageAddr, springboard, 61)
NumPut(0xC2, springboard, 65,"char")
NumPut(4, springboard, 66,"short")

ThreadFunc(wParam, lParam)
{
   global
   CloseHandle(hThread)
   if (!lParam)
   {
	  msgbox "exit", %lParam%, %A_lasterror%
      return 0
   }
   next := 0
   loop
   {
   type := NumGet(outBuffer,4+next)
   if (type == FILE_ACTION_ADDED)
      msg := "Создан файл "
   else if (type = FILE_ACTION_REMOVED)
	  msg := "Удалён файл "
   else if (type = FILE_ACTION_MODIFIED)
	  msg := "Изменён файл "
   else if (type = FILE_ACTION_RENAMED_OLD_NAME)
	  msg := "Файл переименован из "
   else if (type = FILE_ACTION_RENAMED_NEW_NAME)
	  msg := "Файл переименован в "
   else 
      msg := type
   NameLen := NumGet(outBuffer,8+next)
   VarSetCapacity(fn, NameLen+2)
   WriteProcessMemory(GetCurrentProcess(), &fn, &outBuffer+12+next, NameLen, &BytesReturned)
   NumPut(0x0000, fn, NameLen ,"short")
   next := NumGet(outBuffer, next)
   LV_Add("", msg, IfAnsiThenUnicodeToAnsi(fn, str, NameLen) )
   if (!next)
      break
   }
   hThread:=CreateThread(0, 64*1024, &springboard, 0, 0, 0)
   return 0
}

OnMessage(WM_DIRECTORYCHANGE,"ThreadFunc")


PathName := A_ScriptDir
Status := 0
Options := 0
Gui Add, Edit, w200 ReadOnly vE_PathName, %PathName%
Gui Add, Button, x210 y5 w50 vB_PathName gG_PathName, ...
Gui Add, Button, x300 y5 w150 vB_Start gG_Start, Начать слежение
Gui Add, Text, x10, Типы изменений:
Gui Add, Checkbox, x220 y35 vCh_SubDir gG_FILE_NOTIFY_CHANGE, Следить за поддиректориями
Gui Add, Checkbox, x10 y55 w110 vCh_FILE_NAME gG_FILE_NOTIFY_CHANGE, Имена файлов
Gui Add, Checkbox, x120 y55 w110 vCh_DIR_NAME gG_FILE_NOTIFY_CHANGE, Имена папок
Gui Add, Checkbox, x230 y55 w110 vCh_ATTRIBUTES gG_FILE_NOTIFY_CHANGE, Атрибуты
Gui Add, Checkbox, x340 y55 w110 vCh_SIZE gG_FILE_NOTIFY_CHANGE, Размер
Gui Add, Checkbox, x10 y75 w110 vCh_LAST_WRITE gG_FILE_NOTIFY_CHANGE, Запись
Gui Add, Checkbox, x120 y75 w110 vCh_LAST_ACCESS gG_FILE_NOTIFY_CHANGE, Доступ
Gui Add, Checkbox, x230 y75 w110 vCh_CHANGE_CREATION gG_FILE_NOTIFY_CHANGE, Время создания
Gui Add, Checkbox, x340 y75 w110 vCh_CHANGE_SECURITY gG_FILE_NOTIFY_CHANGE, Безопасность
Gui Add, ListView, x10 r20 w450 NoSort, Действие|Имя файла
LV_ModifyCol(1, 146)
LV_ModifyCol(2, 300)
Gui Show
return 

GuiClose:
   ExitApp
return

G_PathName:
   if (Status=1)
   {
      MsgBox Перед сменой директории остановите текущее слежение!
	  return
   }
   Gui Submit, NoHide
   FileSelectFolder PathName , *%E_PathName%, 3, Выбирите папку для наблюдения
   if (PathName!="")
   {
	  GuiControl ,, E_PathName, %PathName%
   }
return

G_Start:
   if (Status=1) ;остановка
   {
      Status := 0
	  TerminateThread(hThread, -1)
	  CloseHandle(hThread)
	  CloseHandle(hDirectory)
	  GuiControl ,, B_Start, Начать слежение
      LV_Add("", "Слежение прервано", PathName)
   }
   else if (Status=0) ;начало
   {
      Gui Submit, NoHide
	  Options := FILE_NOTIFY_CHANGE_FILE_NAME*Ch_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME*Ch_DIR_NAME|FILE_NOTIFY_CHANGE_ATTRIBUTES*Ch_ATTRIBUTES|FILE_NOTIFY_CHANGE_SIZE*Ch_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE*Ch_LAST_WRITE|FILE_NOTIFY_CHANGE_LAST_ACCESS*Ch_LAST_ACCESS|FILE_NOTIFY_CHANGE_CREATION*Ch_CHANGE_CREATION|FILE_NOTIFY_CHANGE_SECURITY*Ch_CHANGE_SECURITY
	  if (Options=0)
	  {
	     MsgBox не выбран ни один тип слежения!
	     return
	  }
	  Status := 1
	  NumPut(Ch_SubDir, springboard, 21)
	  NumPut(Options, springboard, 16)
	  hDirectory := CreateFile(E_PathName, FILE_LIST_DIRECTORY, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)
      if (hDirectory=-1)
      {
         msgbox CreateFile error, %a_lasterror%
         ExitApp
      }
      NumPut(hDirectory, springboard, 36)
	  hThread:=CreateThread(0, 64*1024, &springboard, 0, 0, 0)
	  GuiControl ,, B_Start, Прервать слежение
	  LV_Add("", "Слежение начато", PathName)
   }
   else ;перезапуск
   {
      TerminateThread(hThread, -1)
	  CloseHandle(hThread)
	  CloseHandle(hDirectory)
	  LV_Add("", "Слежение прервано", PathName)
	  Status := 0
	  Goto G_Start
   }
return

G_FILE_NOTIFY_CHANGE:
   if (Status=1)
   {
      Status = -1
	  GuiControl ,, B_Start, Применить изменения
   }
return

и вспомогательный файл "windows.ahk", который должен лежать в папке с основным(в нём только константы и оболочки WinApi функций):

Unicode          := A_IsUnicode ? "W" : "A"

; CreateFile, ShareMode
FILE_SHARE_READ  := 1
FILE_SHARE_WRITE := 2

; CreateFile, DesiredAccess
GENERIC_READ     := 0x80000000
GENERIC_WRITE    := 0x40000000
; дополнительные атрибуты доступа
; тут необходимо указать FILE_LIST_DIRECTORY
FILE_ADD_FILE             := 0x0002
FILE_ADD_SUBDIRECTORY     := 0x0004
FILE_APPEND_DATA          := 0x0004
FILE_CREATE_PIPE_INSTANCE := 0x0004
FILE_DELETE_CHILD         := 0x0040
FILE_EXECUTE              := 0x0020
FILE_LIST_DIRECTORY       := 0x0001 ;право просматривать содержание директории
FILE_READ_ATTRIBUTES      := 0x0080
FILE_READ_DATA            := 0x0001
FILE_READ_EA              := 0x0008
FILE_TRAVERSE             := 0x0020
FILE_WRITE_ATTRIBUTES     := 0x0100
FILE_WRITE_DATA           := 0x0002
FILE_WRITE_EA             := 0x0010

; события получаемые в структуре FILE_NOTIFY_INFORMATION
FILE_ACTION_ADDED            := 0x00000001
FILE_ACTION_REMOVED          := 0x00000002
FILE_ACTION_MODIFIED         := 0x00000003 
FILE_ACTION_RENAMED_OLD_NAME := 0x00000004
FILE_ACTION_RENAMED_NEW_NAME := 0x00000005 

; CreateFile, CreationDisposition
CREATE_NEW        := 1
CREATE_ALWAYS     := 2
OPEN_EXISTING     := 3
OPEN_ALWAYS       := 4
TRUNCATE_EXISTING := 5

; CreateFile, FlagsAndAttributes
FILE_ATTRIBUTE_READONLY   := 0x00000001
FILE_ATTRIBUTE_HIDDEN     := 0x00000002
FILE_ATTRIBUTE_SYSTEM     := 0x00000004
FILE_ATTRIBUTE_DIRECTORY  := 0x00000010
FILE_ATTRIBUTE_ARCHIVE    := 0x00000020
FILE_ATTRIBUTE_NORMAL     := 0x00000080
FILE_ATTRIBUTE_TEMPORARY  := 0x00000100
FILE_ATTRIBUTE_COMPRESSED := 0x00000800
FILE_FLAG_POSIX_SEMANTICS  := 0x01000000
FILE_FLAG_BACKUP_SEMANTICS := 0x02000000
FILE_FLAG_DELETE_ON_CLOSE  := 0x04000000
FILE_FLAG_SEQUENTIAL_SCAN  := 0x08000000
FILE_FLAG_RANDOM_ACCESS    := 0x10000000
FILE_FLAG_NO_BUFFERING     := 0x20000000
FILE_FLAG_OVERLAPPED       := 0x40000000
FILE_FLAG_WRITE_THROUGH    := 0x80000000

FILE_NOTIFY_CHANGE_FILE_NAME   := 0x00000001
FILE_NOTIFY_CHANGE_DIR_NAME    := 0x00000002
FILE_NOTIFY_CHANGE_ATTRIBUTES  := 0x00000004
FILE_NOTIFY_CHANGE_SIZE        := 0x00000008
FILE_NOTIFY_CHANGE_LAST_WRITE  := 0x00000010
FILE_NOTIFY_CHANGE_LAST_ACCESS := 0x00000020
FILE_NOTIFY_CHANGE_CREATION    := 0x00000040
FILE_NOTIFY_CHANGE_SECURITY    := 0x00000100 

GetCurrentProcessId()
{
   return DllCall("GetCurrentProcessId")
}

GetCurrentProcess()
{
   return DllCall("GetCurrentProcess")
}

GetModuleHandle(Name)
{
   global Unicode
   return DllCall("GetModuleHandle" . Unicode, "str", Name)
}

GetProcAddress(hModule, FuncName)
{
   if (!A_IsUnicode)
      return DllCall("GetProcAddress", "int", hModule, "str", FuncName)
   len := StrLen(FuncName)+1
   VarSetCapacity(str, len)
   WideCharToMultiByte(0, 0, &FuncName, -1, &str, len, 0, 0)
   ret := DllCall("GetProcAddress", "int", hModule, "int", &str)
   VarSetCapacity(str, 0)
   return ret
}

WideCharToMultiByte(CodePage, Flags, WideCharStr, WideChar, MultiByteStr, MultiByte, DefaultChar, UsedDefaultChar)
{
   return DllCall("WideCharToMultiByte", "Uint", CodePage, "Uint", Flags, "int", WideCharStr, "int",  WideChar, "int", MultiByteStr, "int", MultiByte, "Uint", DefaultChar, "Uint", UsedDefaultChar)
}

CreateFile(FileName, DesiredAccess, ShareMode, SecurityAttributes, CreationDisposition, FlagsAndAttributes, hTemplateFile)
; FileName- путь к файлу
; DesiredAccess- требуемый доступ(GENERIC_*)
; ShareMode- права на совместный доступ(FILE_SHARE_)
; SecurityAttributes- атрибуты защиты(обычно нуль)
; CreationDisposition- действия над файлом
; FlagsAndAttributes- атрибуты и флаги файла(FILE_ATTRIBUTE_* и FILE_FLAG_*, обычно FILE_ATTRIBUTE_NORMAL)
; hTemplateFile- файл, от которого наследуется FlagsAndAttributes(обычно 0)
{
   global Unicode
   return DllCall("CreateFile" . Unicode, "str", FileName, "int", DesiredAccess, "int", ShareMode, "int", SecurityAttributes, "int", CreationDisposition, "int", FlagsAndAttributes, "int", hTemplateFile)
}

CreateThread(ThreadAttributes, StackSize, StartAddress, Parameter, CreationFlags, ThreadId)
; функция создаёт поток
; ThreadAttributes- атрибуты безопасности, почти всегда 0
; StackSize- начальный размер стека, обычно тоже 0
; StartAddress- адрес функции, которую будет исполнять поток
; Parameter- параметр, который получит функция StartAddress
; CreationFlags- флаги создания, обычно 0, иногда 4(тогда поток создаётся, но не начинает выполняться)
; ThreadId- ссылка на переменную, в которую будет помещён идентификатор потока
{
   return DllCall("CreateThread","int",ThreadAttributes,"int",StackSize,"int",StartAddress,"int",Parameter,"int",CreationFlags,"int",ThreadId)
}

CloseHandle(Handle)
; функция закрывает хэндл Handle
{
   return DllCall("CloseHandle","int",Handle)
}

TerminateThread(hThread, ExitCode)
; функция убивает поток по дескриптору hThread
; ЭТУ ФУНКЦИЮ НЕЛЬЗЯ ИСПОЛЬЗОВАТЬ В ОБЫЧНЫХ ПРИЛОЖЕНИЯХ
{
   return DllCall("TerminateThread", "int", hThread, "int", ExitCode)
}

WriteProcessMemory(hProcess, BaseAddress, Buffer, Size, NumberOfBytesWritten)
; функция записывает Size байт с адреса Buffer в процесс hProcess по адресу BaseAddress
; в NumberOfBytesWritten помещает число записанный байтов
{
   return DllCall("WriteProcessMemory", "int", hProcess, "int", BaseAddress, "int", Buffer, "int", Size, "int", NumberOfBytesWritten)
}

Заодно баг с подсветкой синтаксиса обнаружил . Ложная метка:

Gui Add, Text, x10, Типы изменений:
...

58

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Отлично, всё вроде работает. Добавляй!

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

59

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Александр_ пишет:

Заодно баг с подсветкой синтаксиса обнаружил . Ложная метка:

Gui Add, Text, x10, Типы изменений:
...

Там вообще много всяческих багов. Если пишешь на PHP и есть время, могу прислать исходник, обсудим, что поменять.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

60

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

teadrinker пишет:

Там вообще много всяческих багов. Если пишешь на PHP и есть время, могу прислать исходник, обсудим, что поменять.

Присылай, попробуем разобраться.
P.S. ЛС тут не предусмотрены?

61

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

ОК, попозже пришлю, сейчас пока занят.

Александр_ пишет:

ЛС тут не предусмотрены?

Да был раньше чат, но им никто не пользовался и его удалили за ненадобностью.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

62

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Вариант 2, использующий функцию ReadDirectoryChangesW
При использовании на AHK-L Unicode x64, win7 x64, запуск слежения приводит к:

Faulting application name: AutoHotkey.exe, version: 1.1.5.6, time stamp: 0x4efe44ca
Faulting module name: unknown, version: 0.0.0.0, time stamp: 0x00000000
Exception code: 0xc0000005

Возможно ли успешное использование вышеупомянутого скрипта на x64?

63 (изменено: Александр_, 2012-01-15 22:52:50)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

creature.ws пишет:

Возможно ли успешное использование вышеупомянутого скрипта на x64?

Конечно, только нужно использовать 32-битную версию AHK. Переписать под 64-битную платформу тут не так просто, поскольку изменилась модель вызова функций(т.е. нужно полностью переписывать функцию springboard). Это нужно будет сделать, но не сейчас, ведь ещё только праздники кончились .

64

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Так и не переделали под 64 бит?

65

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Malcev, Вы хотите, чтобы выполнялись действия после появления файла в определённой папке, так? Этот код работает?

66

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Думаю будет.
Сейчас использую следующий код:

file = E:\test.bat

FileGetTime, TimeOrig, %file%
loop
{
   FileGetTime, Time, %file%
   If (Time > TimeOrig)
   {
      TimeOrig := Time
      run, %file%
   }
   sleep, 100
}

Просто Александр_ писал:

Мелкософт рекомендует в таких случаях использовать уведомления и я с ними солидарен:)

67

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Если всё устраивает, то надо ли мудрить?

68

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Наверное вы правы.
Не стоит.

69 (изменено: YMP, 2014-06-14 13:05:52)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Решил всё-таки помудрить и по мотивам скрипта Александра написал версию, работающую для обеих битностей и кодировок (но GUI нет). Алгоритм тот же: ReadDirectoryChangesW запускается через машинную функцию в отдельном потоке и посылает в скрытое окно скрипта сообщения о событиях в папке. Обработка сообщений идёт в функции WM_DIRECTORYCHANGE; она получает адрес выходного буфера с данными о событиях и количество записанных туда байт. В примере ниже просто формируется и выводится сообщение о типе действия и, если определена глобально переменная LogFile, в этот лог пишутся эти сообщения. Фильтр для событий можно задать в функции WatchDirectory в переменной NotifyFilter. Если повторно вызвать WatchDirectory с именем папки, предыдущее слежение будет остановлено автоматически и начато новое. Вызов с нулём — остановка.


DirName := A_ScriptDir
LogFile := A_Desktop . "\DirLog.txt"

F10:: WatchDirectory(DirName)   ; Начать слежение.
F11:: WatchDirectory(0)         ; Остановить.


; ================ Функции =====================================================

WM_DIRECTORYCHANGE(BytesReturned, pOutBuf)
{
    Global LogFile
    ;FILE_ACTION_ADDED := 1, FILE_ACTION_REMOVED := 2, FILE_ACTION_MODIFIED := 3
    ;FILE_ACTION_RENAMED_OLD_NAME := 4, FILE_ACTION_RENAMED_NEW_NAME := 5
    Static Actions := ["Файл добавлен:  ", "Файл удалён:    ", "Файл изменён:   "
                     , "Файл переименован с имени: ", "Файл переименован на имя:  "]
    If (pOutBuf = 0) {
        MsgBox, Ошибка в ReadDirectoryChangeW
        Return
    }
    DateTime := A_DD "." A_MM "." A_YYYY " " A_Hour "." A_Min "." A_Sec
    Addr := pOutBuf, Next := 0
    Loop    ; Чтение из буфера записей о событиях и реакция на них.
    {
        Addr += Next, Next := NumGet(Addr+0, 0, "uint")
        ActionCode := NumGet(Addr+0, 4, "uint")
        If (Action := Actions[ActionCode]) {
            cbFile := NumGet(Addr+0, 8, "uint")
            FileName := StrGet(Addr+12, cbFile // 2, "utf-16")
            Msg .= DateTime " " Action FileName "`n"
        }
        If (!Next)
            Break
    }
    If (Msg) {
        ToolTip, %Msg%
        If (LogFile)
            FileAppend, %Msg%, %LogFile%
        Sleep, 1000
        ToolTip
    }
    WatchDirectory(-1)  ; Продолжить слежение.
}

WatchDirectory(DirName)
{
    Static hDir, hThread, pData, pThreadStart, OutBuf, OutBufSize := 0x400  ; 1 KB
    Static BytesReturned, WM_DIRECTORYCHANGE := 0x401
    ;FILE_NOTIFY_CHANGE_FILE_NAME := 0x1, FILE_NOTIFY_CHANGE_DIR_NAME := 0x2
    ;FILE_NOTIFY_CHANGE_ATTRIBUTES := 0x4, FILE_NOTIFY_CHANGE_SIZE := 0x8
    ;FILE_NOTIFY_CHANGE_LAST_WRITE := 0x10, FILE_NOTIFY_CHANGE_LAST_ACCESS := 0x20
    ;FILE_NOTIFY_CHANGE_CREATION := 0x40, FILE_NOTIFY_CHANGE_SECURITY := 0x100
    Static NotifyFilter := 0x11     ; Комбинация из флагов выше (сумма).
    If (DirName = -1) {
        DllCall("CloseHandle", "ptr", hThread)
        Goto NewThread
    }
    Else If (DirName = 0) {
        If (hThread) {
            DllCall("TerminateThread", "ptr", hThread, "int", 0)
            DllCall("CloseHandle", "ptr", hThread), hThread := 0
        }
        If (hDir)
            DllCall("CloseHandle", "ptr", hDir), hDir := 0
        Return
    }
    If (hDir)
        WatchDirectory(0)
    If (!OutBuf) {
        VarSetCapacity(OutBuf, OutBufSize, 0), VarSetCapacity(BytesReturned, 4, 0)
        OnMessage(WM_DIRECTORYCHANGE, "WM_DIRECTORYCHANGE")
        If !(pReadDirectoryChanges := GetProcAddress("kernel32.dll", "ReadDirectoryChangesW"))
            Return Error("GetProcAddress - ReadDirectoryChangesW")
        If !(pPostMessage := GetProcAddress("user32.dll", "PostMessage" . (A_IsUnicode? "W":"A")))
            Return Error("GetProcAddress - PostMessage")
        If !(pThreadStart := CreateMachineFunc())
            Return Error("CreateMachineFunc")
        pData := CreateStruct(pReadDirectoryChanges, hDir, &OutBuf, OutBufSize, 0
                            , NotifyFilter, &BytesReturned, 0, 0
                            , pPostMessage, A_ScriptHwnd, WM_DIRECTORYCHANGE)
    }
    If !(hDir := OpenDirectory(DirName))
        Return Error("OpenDirectory")
    NumPut(hDir, pData+0, A_PtrSize, "ptr")
NewThread:
    If !(hThread := CreateThread(pThreadStart, pData))
        Return Error("CreateThread")
    Return True
}

OpenDirectory(Dir)
{
    Static FILE_LIST_DIRECTORY := 1, FILE_SHARE_READ := 1, FILE_SHARE_WRITE := 2
    Static OPEN_EXISTING := 3, FILE_FLAG_BACKUP_SEMANTICS := 0x02000000
    Static INVALID_HANDLE_VALUE := -1
    hDir := DllCall("CreateFile", "str", Dir, "uint", FILE_LIST_DIRECTORY
            , "uint", FILE_SHARE_READ | FILE_SHARE_WRITE, "ptr", 0, "uint", OPEN_EXISTING
            , "uint", FILE_FLAG_BACKUP_SEMANTICS, "ptr", 0, "ptr")
    Return hDir = INVALID_HANDLE_VALUE? 0:hDir      
}

CreateStruct(Members*)
{
    Static Struct
    cMembers := Members.MaxIndex()
    VarSetCapacity(Struct, cMembers * A_PtrSize, 0)
    addr := &Struct
    Loop, %cMembers%
        addr := NumPut(Members[A_Index], addr+0, 0, "ptr")
    Return &Struct
}

GetProcAddress(Lib, Func)
{
    hLib := DllCall("LoadLibrary", "str", Lib, "ptr")
    If (hLib = 0)
        Return 0
    Return DllCall("GetProcAddress", "ptr", hLib, "astr", Func, "ptr")
}

CreateMachineFunc()
{
    MEM_RESERVE := 0x2000, MEM_COMMIT := 0x1000, PAGE_EXECUTE_READWRITE := 0x40
    If (A_PtrSize = 8) {
        Hex = 
        ( Join LTrim
        488B0151FF7140FF7138FF7130FF71284883EC204C8B49204C8B4118488B5110488B4908FFD04
        C8B5424404D31C985C04D0F454A10498B4230448B00498B5258498B4A5041FF52484883C448C3
        )
    }
    Else {
        Hex =
        ( Join LTrim
        8B54240452FF7220FF721CFF7218FF7214FF7210FF720CFF7208FF7204FF125A85C00F4542088
        B4A1850FF31FF722CFF7228FF5224C20400
        )
    }
    Len := StrLen(Hex) // 2
    pFunc := DllCall("VirtualAlloc", "ptr", 0, "ptr", Len
                                   , "uint", MEM_RESERVE | MEM_COMMIT
                                   , "uint", PAGE_EXECUTE_READWRITE, "ptr")
    If (pFunc = 0)
        Return 0
    Loop, % Len
        NumPut("0x" . SubStr(Hex, A_Index * 2 - 1, 2), pFunc + 0
                                 , A_Index - 1, "uchar")
    Return pFunc
}

CreateThread(StartAddr, Param)
{
    Return DllCall("CreateThread", "ptr", 0, "ptr", 0, "ptr", StartAddr
                                 , "ptr", Param, "uint", 0, "ptr", 0, "ptr")
}

Error(Func)
{
    MsgBox, %Func% failed.
    Return False
}

70

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Работает! Спасибо!
Может в коллекцию?

71

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Посмотрим. Может, какие-то глюки ещё вылезут.

72

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Исправил ошибку. В функции WatchDirectory, вот тут была опечатка.


If (DirName = -1) {
        DllCall("CloseHandle", "ptr", hThread)
        Goto NewThread
    }

Вместо hThread стояло pThread.

73

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

А почему и при опечатки работало?

74

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Просто хэндл предыдущего потока не освобождался. Запускать новый поток это не мешало. Там в потоке отрабатывает ReadDirectoryChangesW и PostMessage. После этого поток завершается. Функция WM_DIRECTORYCHANGE обрабатывает сообщение, посланное PostMessage, и потом запускает новый поток. Хэндл у него уже другой будет, а старый положено освобождать. Возможно, какие-то ресурсы оставались не освобождёнными из-за этого, но точно не могу сказать.

75

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

YMP так как нет темы для обсуждения, по совету модератора делаю репост здесь.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

76

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

serzh82saratov пишет:

YMP так как нет темы для обсуждения, по совету модератора делаю репост здесь.

Тема есть, вот она.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

77 (изменено: serzh82saratov, 2015-10-24 16:29:00)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Ну обычно она в коллекции и указывается.

----

Вопрос по коду YMP. У меня при каждом событии появляются поочерёдно несколько тултипов, а хотелось бы один.

Как следить за изменениями в подпапках указанной папки?

И ещё, то ли что-то напутал, но кажется был на форуме подобный метод, но на базе ComObj Notification. Не могу найти, или такового и быть не может?

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

78

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

А что такое «ComObj Notification»?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

79

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Это как здесь.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

80

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Так там WMI, а уже где-то обсуждалось, что wmi-подписка на события — это фактически таймер, в отличие от кода выше.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

81

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Да, тоже вспомнил.
Но только здесь наверное тоже придётся ставить таймер на фильтрацию одинаковых сообщений.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

82

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

serzh82saratov пишет:

Вопрос по коду YMP. У меня при каждом событии появляются поочерёдно несколько тултипов, а хотелось бы один.
Как следить за изменениями в подпапках указанной папки?

При каком именно событии и что в тултипах? Конкретика весьма желательна. Вы же сами тут на вопросы отвечаете и должны это понимать. Приведите какой-нибудь пример.

Насчёт подпапок не в курсе. Про эти апишки надо почитать, может, там что есть. Я на данный момент уже забыл подробности этой темы. Поскольку сам такое слежение не использую, не было пока нужды в нём.

83

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

YMP пишет:

Конкретика весьма желательна. Вы же сами тут на вопросы отвечаете и должны это понимать.

Да, прошу прощения, сам не люблю задавать наводящие вопросы, потому как ощущение что оно мне больше надо.
Вообщем при изменении файла 2 тултипа что файл изменен, при создании файла сначала что файл добавлен, потом тултип с 2 строками о изменении этого файла.

Насчёт подпапок не в курсе.

Тут если во вложенной папке есть изменения, то выводится её имя, а если в её папке будут изменения, то уже сообщений никаких нет.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

84

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Вообщем при изменении файла 2 тултипа что файл изменен, при создании файла сначала что файл добавлен, потом тултип с 2 строками о изменении этого файла.

Предполагаю, что второй тултип выводит информацию об изменении лог файла.
Попробуй его закомментировать.

85

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Нет, не в этом дело.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

86

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Странно.
У меня (Unicode 32-bit). OS: Win7x64 и при закоментированном лог-файле выскакивает всегда один тултип.

87

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Это при любом событии?

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

88

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Покопался поглубже.
При создании текстовых файлов .txt - только одно оповещание.
С остальными же 2.

89

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Может ли скрипт использоваться с учётом изменений в под-дерикториях - с выводом полных путей файлов?

90

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Я когда-то писал такое:

#NoEnv
SetBatchLines -1

FILE_NOTIFY_CHANGE_FILE_NAME  := 0x1
FILE_NOTIFY_CHANGE_DIR_NAME   := 0x2
FILE_NOTIFY_CHANGE_ATTRIBUTES := 0x4
FILE_NOTIFY_CHANGE_SIZE       := 0x8

notifyFilter := FILE_NOTIFY_CHANGE_FILE_NAME
              | FILE_NOTIFY_CHANGE_DIR_NAME
              | FILE_NOTIFY_CHANGE_ATTRIBUTES
              | FILE_NOTIFY_CHANGE_SIZE

folderPath1 := A_Desktop
folderPath2 := A_ScriptDir

Inst1 := new FileMonitoring(folderPath1, notifyFilter, "OnDirectoryChanged1", true)
Inst2 := new FileMonitoring(folderPath2, notifyFilter, "OnDirectoryChanged2", false)
Return

OnDirectoryChanged1(filePath, event) {
   MsgBox, % ["Added", "Removed", "Modified", "Renamed, old name", "Renamed, new name"][event] ": " filePath
}

OnDirectoryChanged2(filePath, event) {
   MsgBox, % ["Added", "Removed", "Modified", "Renamed, old name", "Renamed, new name"][event] ": " filePath
}

class FileMonitoring
{
   __New(folderPath, notifyFilter, UserFunc, watchSubtree := false) {
      this.Event := new this._Event()
      this.SetCapacity("buffer", 1024)
      pBuffer := this.GetAddress("buffer")
      this.SetCapacity("overlapped", A_PtrSize*3 + 8)
      this.pOverlapped := this.GetAddress("overlapped")
      this.Directory := new this._ReadDirectoryChanges( folderPath, notifyFilter, watchSubtree
                                                      , pBuffer, this.pOverlapped, this.Event.handle )
      this.EventSignal := new this._EventSignal(this.Directory, this.Event.handle, pBuffer, UserFunc)
      this.Directory.Read()
   }
   
   __Delete() {
      DllCall("CancelIoEx", "Ptr", this.Directory.handle, "Ptr", this.pOverlapped)
      this.Event.Set()
      this.EventSignal.Clear()
      this.Directory.Clear()
      this.SetCapacity("buffer", 0)
      this.buffer := ""
   }
   
   class _Event {
      __New() {
         this.handle := DllCall("CreateEvent", "Int", 0, "Int", 0, "Int", 0, "Int", 0, "Ptr")
      }
      Set() {
         DllCall("SetEvent", "Ptr", this.handle)
      }
      __Delete() {
         DllCall("CloseHandle", "Ptr", this.handle)
      }
   }
   
   class _ReadDirectoryChanges {
      __New(dirPath, notifyFilter, watchSubtree, pBuffer, pOverlapped, hEvent) {
         static OPEN_EXISTING := 3
              , access := (FILE_SHARE_READ := 1) | (FILE_SHARE_WRITE := 2)
              , flags := (FILE_FLAG_OVERLAPPED := 0x40000000) | (FILE_FLAG_BACKUP_SEMANTICS := 0x2000000)
              
         for k, v in ["notifyFilter", "watchSubtree", "pBuffer", "pOverlapped", "hEvent"]
            this[v] := %v%
         this.handle := DllCall("CreateFile", "Str", dirPath, "UInt", 1, "UInt", access, "Int", 0
                                            , "UInt", OPEN_EXISTING, "UInt", flags, "Int", 0, "Ptr")
      }
      
      Read() {
         DllCall("RtlZeroMemory", "Ptr", this.pOverlapped, "Ptr", A_PtrSize*3 + 8)
         NumPut(this.hEvent, this.pOverlapped + A_PtrSize*2 + 8, "Ptr")
         ; there is only a Unicode version of this api
         Return DllCall("ReadDirectoryChangesW", "Ptr", this.handle, "Ptr", this.pBuffer, "UInt", 1024, "UInt", this.watchSubtree
                                               , "UInt", this.notifyFilter, "Ptr", 0, "Ptr", this.pOverlapped, "Ptr", 0)
      }
      
      Clear() {
         DllCall("CloseHandle", "Ptr", this.handle)
      }
   }
   
   class _EventSignal {
      __New(Directory, hEvent, pBuffer, UserFunc) {
         this.WM_EVENTSIGNAL := DllCall("RegisterWindowMessage", "Str", "WM_EVENTSIGNAL", "UInt")
         for k, v in ["Directory", "hEvent", "pBuffer"]
            this[v] := %v%
         this.UserFunc := IsObject(UserFunc) ? UserFunc : Func(UserFunc)
         this.OnEvent := ObjBindMethod(this, "On_WM_EVENTSIGNAL")
         OnMessage(this.WM_EVENTSIGNAL, this.OnEvent)
         this.startAddress := this.CreateWaitFunc(this.hEvent, A_ScriptHwnd, this.WM_EVENTSIGNAL)
         this.Thread := new this._Thread(this.startAddress)
      }
      
      On_WM_EVENTSIGNAL(wp) {
         if !( wp = this.hEvent
            && DllCall("GetOverlappedResult", "Ptr", this.hEvent, "Ptr", this.pBuffer, "UIntP", written, "UInt", false) )
            Return
         
         addr := this.pBuffer
         offset := 0
         Loop {
            addr += offset
            eventType  := NumGet(addr + 4, "UInt")
            objectName := StrGet(addr + 12, NumGet(addr + 8, "UInt")//2, "UTF-16") ; always in Unicode
            timer := this.UserFunc.Bind(objectName, eventType)
            SetTimer, % timer, -10
         } until !offset := NumGet(addr + 0, "UInt")
         this.Thread.Wait()
         this.Thread := new this._Thread(this.startAddress)
         this.Directory.Read()
      }

      CreateWaitFunc(Handle, hWnd, Msg, Timeout := -1) {
         static params := ["UInt", MEM_COMMIT := 0x1000, "UInt", PAGE_EXECUTE_READWRITE := 0x40, "Ptr"]
         ptr := DllCall("VirtualAlloc", "Ptr", 0, "Ptr", A_PtrSize = 4 ? 49 : 85, params*)
         hModule := DllCall("GetModuleHandle", "Str", "kernel32.dll", "Ptr")
         pWaitForSingleObject := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "WaitForSingleObject", "Ptr")
         hModule := DllCall("GetModuleHandle", "Str", "user32.dll", "Ptr")
         pPostMessageW := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "PostMessageW", "Ptr")
         NumPut(pWaitForSingleObject, ptr*1)
         NumPut(pPostMessageW, ptr + A_PtrSize)
         if (A_PtrSize = 4) {
            NumPut(0x68, ptr + 8, "UChar")
            NumPut(Timeout, ptr + 9, "UInt"), NumPut(0x68, ptr + 13, "UChar")
            NumPut(Handle, ptr + 14), NumPut(0x15FF, ptr + 18, "UShort")
            NumPut(ptr, ptr + 20), NumPut(0x6850, ptr + 24, "UShort")
            NumPut(Handle, ptr + 26), NumPut(0x68, ptr + 30, "UChar")
            NumPut(Msg, ptr + 31, "UInt"), NumPut(0x68, ptr + 35, "UChar")
            NumPut(hWnd, ptr + 36), NumPut(0x15FF, ptr + 40, "UShort")
            NumPut(ptr+4, ptr + 42), NumPut(0xC2, ptr + 46, "UChar"), NumPut(4, ptr + 47, "UShort")
         }
         else {
            NumPut(0x53, ptr + 16, "UChar")
            NumPut(0x20EC8348, ptr + 17, "UInt"), NumPut(0xBACB8948, ptr + 21, "UInt")
            NumPut(Timeout, ptr + 25, "UInt"), NumPut(0xB948, ptr + 29, "UShort")
            NumPut(Handle, ptr + 31), NumPut(0x15FF, ptr + 39, "UShort")
            NumPut(-45, ptr + 41, "UInt"), NumPut(0xB849, ptr + 45, "UShort")
            NumPut(Handle, ptr + 47), NumPut(0xBA, ptr + 55, "UChar")
            NumPut(Msg, ptr + 56, "UInt"), NumPut(0xB948, ptr + 60, "UShort")
            NumPut(hWnd, ptr + 62), NumPut(0xC18941, ptr + 70, "UInt")
            NumPut(0x15FF, ptr + 73, "UShort"), NumPut(-71, ptr + 75, "UInt")
            NumPut(0x20C48348, ptr + 79, "UInt"), NumPut(0xC35B, ptr + 83, "UShort")
         }
         Return ptr + A_PtrSize*2
      }
      
      class _Thread {
         __New(startAddress) {
            if !this.handle := DllCall("CreateThread", "Int", 0, "Int", 0, "Ptr", startAddress, "Int", 0, "UInt", 0, "Int", 0, "Ptr")
               throw Exception("Failed to create thread.`nError code: " . A_LastError)
         }
         Wait() {
            DllCall("WaitForSingleObject", "Ptr", this.handle, "Int", -1)
         }
         __Delete() {
            DllCall("CloseHandle", "Ptr", this.handle)
         }
      }
      
      Clear() {
         this.Thread.Wait()
         OnMessage(this.WM_EVENTSIGNAL, this.OnEvent, 0)
         this.OnEvent := ""
         DllCall("VirtualFree", "Ptr", this.startAddress - A_PtrSize*2, "Ptr", A_PtrSize = 4 ? 49 : 85, "UInt", MEM_DECOMMIT := 0x4000)
      }
   }
}

Последний параметр (true/false) отвечает за мониторинг подпапок.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

91

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Спасибо!

92

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Как задать отображение полного пути папки folderPath1/folderPath2, в которой произошло изменение? Чтобы отображение было в формате полного пути, второй вариант? -

Added: Новая папка\Новый текстовый документ.txt
Added: F:\TMP\Новая папка\Новый текстовый документ.txt

93

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

А приставить путь к корневой папке не вариант?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

94

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Точно, вот так заработало:

OnDirectoryChanged1(filePath, event) {
   global folderPath1
   res := % ["Added", "Removed", "Modified", "Renamed, old name", "Renamed, new name"][event] ": " filePath
   MsgBox, %folderPath1%\%res%
}

95

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Первый знак процента лишний.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

96 (изменено: Malcev, 2023-06-07 07:20:05)

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

В коде из 90 поста стоило бы OnMessage заменить на RegisterCallback(), чтобы не терялись оповещания во время открытия меню.
https://www.autohotkey.com/boards/viewt … 64#p492064

97

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Наверно можно, но я бы эту функцию в таком виде вообще не использовал. Оказалось, что она не буфферизирует или не отлавливает множество изменений, произошедших за короткое время, например, переименование сразу большой группы файлов. Туда нужно WaitForMultipleObjects вместо WaitForSingleObject присобачить.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

98

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Тогда проще watchfolder использовать:
https://www.autohotkey.com/boards/viewtopic.php?t=8384

99

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Та функция мне не нравится тем, что там не используется полноценно механизм событий, там таймер.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

100

Re: AHK: Запуск скрипта не по кнопке, а при событии создания файла

Но зато события не теряются.