Code: Select all
DWORD __fastcall ThreadProc(LPVOID lpParameter) {
DWORD WaitEvent = WaitForSingleObject(Handle, Timeout);
return (DWORD)SendMessageW(hWnd, Msg, (WPARAM)Handle, WaitEvent);
}
Code: Select all
DWORD __fastcall ThreadProc(LPVOID lpParameter) {
DWORD WaitEvent = WaitForSingleObject(Handle, Timeout);
return (DWORD)SendMessageW(hWnd, Msg, (WPARAM)Handle, WaitEvent);
}
Code: Select all
#NoEnv
SetBatchLines -1
folderPath1 := A_Desktop
folderPath2 := A_ScriptDir
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
OnDirectoryChanges(folderPath1, "UserFunc", notifyFilter)
OnDirectoryChanges(folderPath2, "UserFunc", notifyFilter)
UserFunc(filePath, action) {
MsgBox % A_ThisFunc . "`n" . ["Added", "Removed", "Modified", "Renamed, old name", "Renamed, new name"][action] ": " filePath
}
OnDirectoryChanges(dirPath, procName := -1, notifyFilter := 3) {
static DirList := {}, EventList, WM_ReadDirectoryChanges := 0xFFF
IsObject(EventList) || EventList := DirectoryChangesEventListener(-1, 0, 0)
if (procName = -1) {
return EventList[DirList[dirPath]].UserDefinedProc.Name
}
else if (procName = "") {
if DirList.HasKey(dirPath) && eventHandle := DirList.Delete(dirPath)
return EventList.Delete(eventHandle).UserDefinedProc.Name
}
else if IsFunc(procName) {
if DirList.HasKey(dirPath) && eventHandle := DirList.Delete(dirPath)
priorProcName := EventList.Delete(eventHandle).UserDefinedProc.Name
else priorProcName := procName
if OnMessage(WM_ReadDirectoryChanges) != "DirectoryChangesEventListener"
OnMessage(WM_ReadDirectoryChanges, "DirectoryChangesEventListener")
Event := new _Event
Event.Proc := new _AsyncReadDirectoryChanges(dirPath, notifyFilter)
Event.Wait := new _AsyncWaitFunc(Event.handle, A_ScriptHWND, WM_ReadDirectoryChanges)
Event.UserDefinedProc := Func(procName)
Event.dirPath := dirPath
DirList[dirPath] := Event.handle
EventList[Event.handle] := Event
Event.Proc()
Event.Wait()
return priorProcName
}
else return false
}
DirectoryChangesEventListener(wParam, lParam, msg) {
static EventList := {}, WM_ReadDirectoryChanges := 0xFFF
if (msg = WM_ReadDirectoryChanges && Event := EventList[wParam]) {
if !DllCall("GetOverlappedResult", "Ptr", Event.Proc.handle, "Ptr", Event.Proc.GetAddress("buffer"), "UIntP", written, "UInt", true)
Return
action := Event.Proc.GetEventType()
name := Event.Proc.GetObjectName()
path := Event.dirPath . "\" . name
Proc := Event.UserDefinedProc
%Proc%(path, action)
Event.Proc()
Event.Wait()
}
else if (wParam = -1)
return EventList
}
class _Event
{
__New() {
this.handle := DllCall("CreateEvent", "Int", 0, "Int", 0, "Int", 1, "Int", 0, "Ptr")
this.SetCapacity("overlapped", A_PtrSize*3 + 8)
addr := this.GetAddress("overlapped")
DllCall("RtlZeroMemory", "Ptr", addr, "Ptr", A_PtrSize*3 + 8)
NumPut(this.handle, addr + A_PtrSize*2 + 8, "Ptr")
}
__Delete() {
DllCall("CloseHandle", "Ptr", this.handle)
}
}
class _AsyncWaitFunc
{
__New(eventObjHandle, hWnd, msg, timeout := -1) {
if !this.startAddress := CreateWaitFunc(eventObjHandle, hWnd, msg, timeout)
throw Exception("Failed to create wait function.`n`nError code:`t" . A_LastError)
}
__Call(EventObj) {
if IsObject(EventObj) {
if this.threadHandle {
DllCall("TerminateThread", "Ptr", this.threadHandle, "UInt", 0)
DllCall("CloseHandle", "Ptr", this.threadHandle)
}
if !this.threadHandle := DllCall("CreateThread", "Int", 0, "Int", 0, "Ptr", this.startAddress, "Int", 0, "UInt", 0, "Int", 0, "Ptr")
throw Exception("Failed to create thread.`n`nError code:`t" . A_LastError)
return this.threadHandle
}
}
__Delete() {
if this.threadHandle {
DllCall("TerminateThread", "Ptr", this.threadHandle, "UInt", 0)
DllCall("CloseHandle", "Ptr", this.threadHandle)
}
}
}
class _AsyncReadDirectoryChanges
{
__New(dirPath, notifyFilter) {
static FILE_SHARE_READ := 1
, FILE_SHARE_WRITE := 2
, OPEN_EXISTING := 3
, FILE_FLAG_OVERLAPPED := 0x40000000
, FILE_FLAG_BACKUP_SEMANTICS := 0x02000000
this.SetCapacity("buffer", 1024)
this.notifyFilter := notifyFilter
this.handle := DllCall("CreateFile", "Str", dirPath, "UInt", 1, "UInt", FILE_SHARE_READ|FILE_SHARE_WRITE, "Int", 0
, "UInt", OPEN_EXISTING
, "UInt", FILE_FLAG_OVERLAPPED|FILE_FLAG_BACKUP_SEMANTICS, "Int", 0, "Ptr")
}
GetEventType() {
return NumGet(this.GetAddress("buffer") + 4, "UInt")
}
GetObjectName() {
bufferRef := this.GetAddress("buffer")
return StrGet(bufferRef + 12, NumGet(bufferRef + 8, "UInt")//2)
}
__Call(EventObj) {
if IsObject(EventObj) {
return DllCall("ReadDirectoryChanges", "Ptr", this.handle, "Ptr", this.GetAddress("buffer"), "UInt", 1024, "Int", 1
, "UInt", this.notifyFilter, "UInt", 0, "Ptr", EventObj.GetAddress("overlapped"), "Int", 0)
}
}
__Delete() {
DllCall("CloseHandle", "Ptr", this.handle)
}
}
CreateWaitFunc(Handle, hWnd, Msg, Timeout=-1)
{
static MEM_COMMIT := 0x1000, PAGE_EXECUTE_READWRITE := 0x40
ptr := DllCall("VirtualAlloc", "Ptr", 0, "Ptr", A_PtrSize = 4 ? 49 : 85, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
hModule := DllCall("GetModuleHandle", "Str", "kernel32.dll", "Ptr")
pWaitForSingleObject := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "WaitForSingleObject", "Ptr")
hModule := DllCall("GetModuleHandle", "Str", "user32.dll", "Ptr")
pSendMessageW := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "SendMessageW", "Ptr")
NumPut(pWaitForSingleObject, ptr*1)
NumPut(pSendMessageW, 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
}
The code made by Александр_.
Code: Select all
; Функция потока
; Ожидает возврата из 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
Code: Select all
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) ; Если смещение равно 0, больше записей в буфере нет.
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
}
Code: Select all
#if x64
mov rax,[rcx]
push rcx,[rcx+40h],[rcx+38h],[rcx+30h],[rcx+28h]
sub rsp,20h
mov r9,[rcx+20h]
mov r8,[rcx+18h]
mov rdx,[rcx+10h]
mov rcx,[rcx+8h]
call rax ; Вызов ReadDirectoryChangeW
mov r10,[rsp+40h]
xor r9,r9
test eax,eax
cmovnz r9,[r10+10h]
mov rax,[r10+30h]
mov r8d,[rax]
mov rdx,[r10+58h]
mov rcx,[r10+50h]
call [r10+48h] ; Вызов PostMessage
add rsp,48h
ret
#else
mov edx,[esp+4]
push edx
push [edx+20h],[edx+1Ch],[edx+18h],[edx+14h],[edx+10h],[edx+0Ch],[edx+8h],[edx+4h]
call [edx]
pop edx
test eax,eax
cmovnz eax,[edx+8h]
mov ecx,[edx+18h]
push eax,[ecx],[edx+2Ch],[edx+28h]
call [edx+24h]
ret 4
#endif
Code: Select all
#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")
Inst2 := new FileMonitoring(folderPath2, notifyFilter, "OnDirectoryChanged2")
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) {
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, 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 := ""
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")
}
__Delete() {
DllCall("SetEvent", "Ptr", this.handle)
Sleep, 20
DllCall("CloseHandle", "Ptr", this.handle)
}
}
class _ReadDirectoryChanges {
__New(dirPath, notifyFilter, pBuffer, pOverlapped, hEvent) {
static access := (FILE_SHARE_READ := 1) | (FILE_SHARE_WRITE := 2), OPEN_EXISTING := 3
, flags := (FILE_FLAG_OVERLAPPED := 0x40000000) | (FILE_FLAG_BACKUP_SEMANTICS := 0x2000000)
for k, v in ["notifyFilter", "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("msvcrt\memset", "Ptr", this.pOverlapped, "Int", 0, "Ptr", A_PtrSize*3 + 8, "Cdecl")
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, "Int", 1
, "UInt", this.notifyFilter, "UInt", 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 := new this._Thread(this.startAddress)
this.Directory.Read()
}
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)
}
__Delete() {
DllCall("CloseHandle", "Ptr", this.handle)
}
}
Clear() {
this.Thread := ""
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)
}
CreateWaitFunc(Handle, hWnd, Msg, Timeout=-1)
{
static MEM_COMMIT := 0x1000, PAGE_EXECUTE_READWRITE := 0x40
ptr := DllCall("VirtualAlloc", "Ptr", 0, "Ptr", A_PtrSize = 4 ? 49 : 85, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
hModule := DllCall("GetModuleHandle", "Str", "kernel32.dll", "Ptr")
pWaitForSingleObject := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "WaitForSingleObject", "Ptr")
hModule := DllCall("GetModuleHandle", "Str", "user32.dll", "Ptr")
pSendMessageW := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "SendMessageW", "Ptr")
NumPut(pWaitForSingleObject, ptr*1)
NumPut(pSendMessageW, 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
}
}
}
I only browsed it, it seems fine. You need
Code: Select all
this.onevent := ""
See [a109] watch_folder.I think the simplest way to use ReadDirectoryChangeW is to specify a completion routine. I will write a function for v2 to demonstrate when I get time.
Code: Select all
; Not real code
h := CreateFile()
ReadDirectoryChangesW(h, ..., registerCallback("MyCallback"))
settimer "SleepEx"
You can, without any fear, change this lineteadrinker wrote: Yeah, I thought about a SendMessage's delay, but unfortunately I have a not enough knowledge to rewrite the CreateWaitFunc. Can you help, maybe? It's a great thing, and could be used for many different purposes.
Code: Select all
pSendMessageW := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "SendMessageW", "Ptr")
Code: Select all
pSendMessageW := DllCall("GetProcAddress", "Ptr", hModule, "AStr", "PostMessageW", "Ptr")
Code: Select all
#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")
Inst2 := new FileMonitoring(folderPath2, notifyFilter, "OnDirectoryChanged2")
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) {
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, 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 := ""
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")
}
__Delete() {
DllCall("SetEvent", "Ptr", this.handle)
Sleep, 20
DllCall("CloseHandle", "Ptr", this.handle)
}
}
class _ReadDirectoryChanges {
__New(dirPath, notifyFilter, pBuffer, pOverlapped, hEvent) {
static access := (FILE_SHARE_READ := 1) | (FILE_SHARE_WRITE := 2), OPEN_EXISTING := 3
, flags := (FILE_FLAG_OVERLAPPED := 0x40000000) | (FILE_FLAG_BACKUP_SEMANTICS := 0x2000000)
for k, v in ["notifyFilter", "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("msvcrt\memset", "Ptr", this.pOverlapped, "Int", 0, "Ptr", A_PtrSize*3 + 8, "Cdecl")
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, "Int", 1
, "UInt", this.notifyFilter, "UInt", 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 := new this._Thread(this.startAddress)
this.Directory.Read()
}
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)
}
__Delete() {
DllCall("WaitForSingleObject", "Ptr", this.handle, "Int", -1)
DllCall("CloseHandle", "Ptr", this.handle)
}
}
Clear() {
this.Thread := ""
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)
}
CreateWaitFunc(Handle, hWnd, Msg, Timeout=-1)
{
static MEM_COMMIT := 0x1000, PAGE_EXECUTE_READWRITE := 0x40
ptr := DllCall("VirtualAlloc", "Ptr", 0, "Ptr", A_PtrSize = 4 ? 49 : 85, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
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
}
}
}
Code: Select all
#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")
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")
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(hEvent, hWnd, msg, timeout := -1) {
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")
pWaitForObj := DllCall("GetProcAddress" , "Ptr", hModule, "AStr", "WaitForSingleObject", "Ptr")
hModule := DllCall("GetModuleHandle", "Str", "user32.dll", "Ptr")
pPostMessage := DllCall("GetProcAddress" , "Ptr", hModule, "AStr", "PostMessageW", "Ptr")
NumPut(pWaitForObj , ptr + 0)
NumPut(pPostMessage, ptr + A_PtrSize)
if (A_PtrSize = 4) {
NumPut(0x68 , ptr + 8)
NumPut(timeout, ptr + 9) , NumPut(0x68 , ptr + 13)
NumPut(hEvent , ptr + 14) , NumPut(0x15FF, ptr + 18)
NumPut(ptr , ptr + 20) , NumPut(0x6850, ptr + 24)
NumPut(hEvent , ptr + 26) , NumPut(0x68 , ptr + 30)
NumPut(msg , ptr + 31) , NumPut(0x68 , ptr + 35)
NumPut(hWnd , ptr + 36) , NumPut(0x15FF, ptr + 40)
NumPut(ptr + 4, ptr + 42) , NumPut(0xC2 , ptr + 46, "UChar")
NumPut(4 , ptr + 47, "UShort")
}
else {
NumPut(0x53 , ptr + 16)
NumPut(0x20EC8348, ptr + 17) , NumPut(0xBACB8948, ptr + 21)
NumPut(timeout , ptr + 25) , NumPut(0xB948 , ptr + 29)
NumPut(hEvent , ptr + 31) , NumPut(0x15FF , ptr + 39)
NumPut(-45 , ptr + 41) , NumPut(0xB849 , ptr + 45)
NumPut(hEvent , ptr + 47) , NumPut(0xBA , ptr + 55)
NumPut(msg , ptr + 56) , NumPut(0xB948 , ptr + 60)
NumPut(hWnd , ptr + 62) , NumPut(0xC18941 , ptr + 70)
NumPut(0x15FF , ptr + 73) , NumPut(-71 , ptr + 75)
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)
}
}
}
2 comments
Actually it's unnecessary, this.Event is freed anyway.icc2icc wrote:but when I added back this.Event := "" to the __delete section it worked