【AHKv2】文件夹监控-大白

注意:这是ahkv2版本的脚本 

ui := Gui.New(), ui.MarginX := ui.MarginY := 20
ui.Add("Text", , '文件夹监视'), ui.Add("Edit", 'xm y+5 w730 cGray +ReadOnly vFolder', A_Desktop)
ui.Add("Button", 'x+m yp w50 +Default', '...').OnEvent('Click', 'SelectFolder')
ui.Add("Text", 'xm y+10', '选项')
for _ in [['xm y+10 vSubTree', '子目录'], ['x+5 yp vFiles Checked', '文件名'], ['x+5 yp vFolders Checked', '目录名'], ['x+5 yp vAttributes', '属性'],
	['x+5 yp vSize', '文件大小'], ['x+5 yp vLast_write', '最后写入时间'], ['x+5 yp vLast_access', '最后访问时间'], ['x+5 yp vCreation', '创建时间'], ['x+5 yp vSecurity', '安全描述符']]
	ui.Add("Checkbox", _[1], _[2])
ui.Add("ListView", 'xm w800 r15 vLV', ['修改时间','路径','行为','文件(夹)名','旧文件(夹)名',' '])
ui.Add("Button", 'xm w100 vStart', '开始').OnEvent('Click', 'StartorStop')
ui.Add("Button", 'x+m yp wp vStop +Disabled', '停止').OnEvent('Click', 'StartorStop')
ui.Add("Button", 'x+m yp wp', '清除').OnEvent('Click', (ctl, *) => ctl.Gui['LV'].Delete())
ui.Title := '文件夹监视', ui.Show(), ui.OnEvent('Close', (*) => ExitApp())
return

SelectFolder(ctl, *) {
	ui := ctl.Gui
	if (folder := DirSelect())
		ui['Folder'].Text := folder, ui['Start'].Enabled := true
}

StartorStop(ctl, *) {
	static mon := '', _ := OnExit((*) => (mon := ''))
	ui := ctl.Gui
	if (ctl.Name = 'Start') {
		ui.Submit(false), filter := 0
		if !InStr(FileExist(ui['Folder'].Text), 'D')
			return MsgBox(ui['Folder'].Text '不是一个有效的文件夹', 'Error')
		for i, v in ['Files', 'Folders', 'Attributes', 'Size', 'Last_write', 'Last_access', 'Creation', 'Security']
			filter |= ui[v].Value ? [1, 2, 4, 8, 16, 32, 64, 256][i] : 0
		mon := FileMonitoring.New(ui['Folder'].Text, filter, Func('update'), ui['SubTree'].Value), ui['Start'].Enabled := !(ui['Stop'].Enabled := true)
	} else mon := '', ui['Start'].Enabled := !(ui['Stop'].Enabled := false)
	update(change) {
		ui['LV'].Add('vis', FormatTime(, 'yyyy/MM/dd hh:mm:ss'), ui['Folder'].Text, ['新建', '删除', '修改', '重命名'][change.Action], change.Name, change.HasOwnProp('OldName') ? change.OldName : '', '')
		ui['LV'].ModifyCol()
	}
}

class FileMonitoring {
	static NOTIFY := {FILE_NAME: 0x1, DIR_NAME: 0x2, ATTRIBUTES: 0x4, CHANGE_SIZE: 0x8,
		LAST_WRITE: 0x10, LAST_ACCESS: 0x20, CREATION: 0x40, SECURITY: 0x100}	; FILE_NOTIFY_CHANGE
	__New(folderPath, notifyFilter, UserFunc, watchSubtree := false) {
		this.Event := FileMonitoring._Event.New(), this.buffer := BufferAlloc(1024), pBuffer := this.buffer.Ptr, this.overlapped := BufferAlloc(A_PtrSize * 3 + 8), this.pOverlapped := this.overlapped.Ptr
		this.Directory := FileMonitoring._ReadDirectoryChanges.New(folderPath, notifyFilter, watchSubtree, pBuffer, this.pOverlapped, this.Event.Ptr)
		this.EventSignal := FileMonitoring._EventSignal.New(this.Directory, this.Event.Ptr, pBuffer, UserFunc), this.EventSignal.folderPath := folderPath, this.Directory.Read()
	}

	__Delete() {
		DllCall('CancelIoEx', 'Ptr', this.Directory.Handle, 'Ptr', this.pOverlapped)
		this.Event.Set(), this.EventSignal.Clear(), this.Directory.Clear(), this.buffer := ''
	}

	class _Event {
		__New() => (this.Ptr := DllCall('CreateEvent', 'Int', 0, 'Int', 0, 'Int', 0, 'Int', 0, 'Ptr'))
		Set() => DllCall('SetEvent', 'Ptr', this)
		__Delete() => DllCall('CloseHandle', 'Ptr', this)
	}

	class _EventSignal {
		__New(Directory, hEvent, pBuffer, UserFunc) {
			for v in ['Directory', 'hEvent', 'pBuffer']
				this.%v% := %v%
			OnMessage(this.WM_EVENTSIGNAL := DllCall('RegisterWindowMessage', 'Str', 'WM_EVENTSIGNAL', 'UInt'), this.OnEvent := ObjBindMethod(this, 'On_WM_EVENTSIGNAL'))
			this.UserFunc := IsObject(UserFunc) ? UserFunc : Func(UserFunc), this.startAddress := this.CreateWaitFunc(this.hEvent, A_ScriptHwnd, this.WM_EVENTSIGNAL)
			this.Thread := FileMonitoring._EventSignal._Thread.New(this.startAddress)
		}

		On_WM_EVENTSIGNAL(wp, *) {
			if !(wp = this.hEvent && DllCall('GetOverlappedResult', 'Ptr', this.hEvent, 'Ptr', this.pBuffer, 'UInt*', written := 0, 'UInt', false))
				return
			addr := this.pBuffer, offset := 0, preName := '', preAction := 0
			Loop {
				addr += offset, Action := NumGet(addr + 4, 'UInt'), Name := StrGet(addr + 12, NumGet(addr + 8, 'UInt') // 2, 'UTF-16')
				if (Name == preName && Action == preAction)
					continue
				if (Action = 4) {
					Change := {Action: Action, OldName: Name}, preName := Name, preAction := Action
					continue
				} else if (Action = 5 && preAction = 4) {
					Change.Path := this.folderPath '' Name, Change.Name := Name
				} else Change := {Action: Action, Name: Name, Path: this.folderPath '' Name}
				SetTimer(this.UserFunc.Bind(Change), -1), preName := Name, preAction := Action
			} until !(offset := NumGet(addr + 0, 'UInt'))
			this.Thread.Wait(), this.Thread := FileMonitoring._EventSignal._Thread.New(this.startAddress), this.Directory.Read()
		}

		CreateWaitFunc(Handle, hWnd, Msg, Timeout := -1) {
			ptr := DllCall('VirtualAlloc', 'Ptr', 0, 'Ptr', A_PtrSize = 4 ? 49 : 85, 'UInt', MEM_COMMIT := 0x1000, 'UInt', PAGE_EXECUTE_READWRITE := 0x40, 'Ptr')
			pWaitForSingleObject := DllCall('GetProcAddress', 'Ptr', DllCall('GetModuleHandle', 'Str', 'kernel32.dll', 'Ptr'), 'AStr', 'WaitForSingleObject', 'Ptr')
			pPostMessageW := DllCall('GetProcAddress', 'Ptr', DllCall('GetModuleHandle', 'Str', 'user32.dll', 'Ptr'), 'AStr', 'PostMessageW', 'Ptr')
			NumPut('Ptr', pWaitForSingleObject, 'Ptr', pPostMessageW, ptr + 0)
			if (A_PtrSize = 4) {
				NumPut('UChar', 0x68, ptr + 8), NumPut('UInt', Timeout, ptr + 9), NumPut('UChar', 0x68, ptr + 13)
				NumPut('Ptr', Handle, ptr + 14), NumPut('UShort', 0x15FF, ptr + 18), NumPut('Ptr', ptr, ptr + 20)
				NumPut('UShort', 0x6850, ptr + 24), NumPut('Ptr', Handle, ptr + 26), NumPut('UChar', 0x68, ptr + 30)
				NumPut('UInt', Msg, ptr + 31), NumPut('UChar', 0x68, ptr + 35), NumPut('Ptr', hWnd, ptr + 36), NumPut('UShort', 0x15FF, ptr + 40)
				NumPut('Ptr', ptr + 4, ptr + 42), NumPut('UChar', 0xC2, ptr + 46), NumPut('UShort', 4, ptr + 47)
			} else {
				NumPut('UChar', 0x53, ptr + 16), NumPut('UInt', 0x20EC8348, ptr + 17), NumPut('UInt', 0xBACB8948, ptr + 21)
				NumPut('UInt', Timeout, ptr + 25), NumPut('UShort', 0xB948, ptr + 29), NumPut('Ptr', Handle, ptr + 31)
				NumPut('UShort', 0x15FF, ptr + 39), NumPut('UInt', -45, ptr + 41), NumPut('UShort', 0xB849, ptr + 45)
				NumPut('Ptr', Handle, ptr + 47), NumPut('UChar', 0xBA, ptr + 55), NumPut('UInt', Msg, ptr + 56), NumPut('UShort', 0xB948, ptr + 60)
				NumPut('Ptr', hWnd, ptr + 62), NumPut('UInt', 0xC18941, ptr + 70), NumPut('UShort', 0x15FF, ptr + 73)
				NumPut('UInt', -71, ptr + 75), NumPut('UInt', 0x20C48348, ptr + 79), NumPut('UShort', 0xC35B, ptr + 83)
			}
			return ptr + A_PtrSize * 2
		}

		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)
		}

		class _Thread {
			__New(startAddress) {
				if !(this.handle := DllCall('CreateThread', 'Ptr', 0, 'UInt', 0, 'Ptr', startAddress, 'Ptr', 0, 'UInt', 0, 'UInt', 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)
		}
	}

	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 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('Ptr', this.hEvent, this.pOverlapped + A_PtrSize * 2 + 8)
			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)
	}
}

给TA捐赠
共{{data.count}}人
人已捐赠
其他

精确计时-顽石沉风

2021-5-22 21:42:52

其他教程

输出调试信息到控制台-feiyue

2021-5-25 16:34:33

2 条回复 A文章作者 M管理员
  1. arwar88

    学习

  2. 逆风

    V2??版本太多也不是什么好事,脚本不能共享,语法有差异

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索