Что нового

Отслеживание изменений в файле

ViktorSPB

Новичок
Сообщения
109
Репутация
0
Добрый день!
Не судите строго, только начинаю разбираться.
Задача. Хочу написать блок, который позволит генерить сигналы, не важно какие пока, хоть мессэдж выкидывать, при изменении указанного файла. Похожие темы уже видел на форуме, но там были некоторые как мне кажется, несоответствия моей задаче. Мои изменения будут происходить помногу раз в день, т.е. скрипт должен работать постоянно. А я пока не понял архитектуру и принципы построения программ в AutoIt. Можно ли, например, прописать срабатывание условия при появлении события, или придется зацикливать и сравнивать с предыдущим значением даты/времени изменения файла? Как скажется на системе подобное зацикливание, не нагрузит ли проц лишней обработкой?
Думаю суть своих проблем передал, помогите, пожалуйста, подскажите где копать, я бы разобрался. Стыдно просить готовое решение, я ведь пока не смог разобраться с тем, что на форуме есть, пока для меня сложновато, хоть и опыт есть в скриптовых языках, и хэлп прочитал, вот уроки сейчас смотрю.. Буду признателен за помощь.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
ViktorSPB,
Один из вариантов.
Код:
#include <WinAPIEx.au3>;Yashied, http://autoit-script.ru/index.php/topic,47.0.html

Opt('MustDeclareVars', 1)
Opt('TrayMenuMode', 1)

Global $sFile = @ScriptDir & '\Test.txt', _ ;путь к файлу, за которым будем смотреть;
		$iTimeOut = 5000, _ ;5 сек. перерыв между проверками;
		$sTimeModifiedOLd, _ ;дата и время предыдущего изменения файла;
		$sTimeModified, _ ;дата и время изменения файла;
		$iStart

HotKeySet('{Esc}', '_Exit') ;назначаем горячую клавишу (Esc) на выход;
If Not FileExists($sFile) Then ;если файла нет, то выход;
	MsgBox(16, 'Error', 'Отсутствует файл' & @LF & $sFile)
	Exit
EndIf
$sTimeModifiedOLd = FileGetTime($sFile, 0, 1);получаем дату и время изменения файла;

$iStart = TimerInit();запускаем таймер;
_WinAPI_EmptyWorkingSet() ;см. в справке к WinAPIEx.au3;
While 1 ;запускаем бесконечный цикл
	If TimerDiff($iStart) >= $iTimeOut Then ;если прошло 5 сек.;
		If FileExists($sFile) Then ;проверяем, что файл на месте;
			$sTimeModified = FileGetTime($sFile, 0, 1);получаем дату и время изменения файла;
			If $sTimeModifiedOLd <> $sTimeModified Then;если значение изменилось;
				;или делаем то, что нам нужно;
				TrayTip('Test', 'Файл был изменен ' & @LF & @HOUR & ':' & @MIN & ':' & @SEC, 3, 2);выводим сообщение;
				$sTimeModifiedOLd = $sTimeModified ;присваиваем старому значению новое;
			EndIf
		EndIf
		$iStart = TimerInit();запускаем таймер;
	EndIf
	Sleep(100) ;пауза, чтобы не грузить процессор.
WEnd

Func _Exit()
	Exit
EndFunc   ;==>_Exit
 
Автор
V

ViktorSPB

Новичок
Сообщения
109
Репутация
0
Спасибо, большое. Все это время разбираюсь. Библиотеку подключил, все работает. Никак не могу победить выход не через эскейп а через кнопку на вызываемом окне. Попытался свою функцию прописать, заругался дебуггер.. Но с этим, думаю, справлюсь, у меня другой вопрос, почему эдитор то дает скрипт запустить то не дает? даже GO не подсвечивается.. Никаких ошибок не подсвечивает вроде... перезапустил - все заработало... уфф.. да, с ним разбираться надо,везде свои глюки.
_WinAPI_EmptyWorkingSet я так понимаю отключает что-то не нужное, чтобы систему не загружать? Читал хэлп, но он мне не прояснил ситуацию.(Удаляет столько страниц, сколько можно из рабочего набора указанного процесса. - перевод гугла)


Добавлено:
Сообщение автоматически объединено:

Победил прекращение с кнопки в окне) Спасибо еще раз! Думаю, эта тема закрыта, вопрос решен. :smile:
 

VladUs

Скриптер
Сообщения
621
Репутация
181
Вот способ, основанный на проверке контрольной суммы файла (CRC32), т.е
на основе проверки изменения содержимого файла. Если файл изменен, контрольная сумма файла меняется, следовательно это можно использовать для отслеживания за состоянием файла.
Код:
Код:
#Include <CRC.au3>

Global $BufferSize = 0x20000
Global $CRC32 = 0
Global $RetM = 0

_Repair ()

AdlibRegister("_CRC32Ret",5000)

While 1
   Sleep (300)
WEnd

; Функция считывает CRC32 и выводит сообщение если файл изменен
Func _CRC32Ret ()
   $Filename = "C:\Test.txt"
   $FileHandle = FileOpen($Filename, 16)
   
For $i = 1 To Ceiling(FileGetSize($Filename) / $BufferSize)
	$Data = FileRead($FileHandle, $BufferSize)
	$Ret = _CRC32($Data, BitNot($CRC32))
Next

FileClose($FileHandle)

If $Ret <> $RetM Then
MsgBox(0, "Result", "CRC32: " & Hex($Ret, 8) & @CRLF & "Файл изменен !")
$RetM = $Ret
EndIf

EndFunc

; Функция при запуске скрипта заносит в глобальную переменную CRC32 файла
Func _Repair ()
   $Filename = "C:\Test.txt"
   $FileHandle = FileOpen($Filename, 16)
   
For $i = 1 To Ceiling(FileGetSize($Filename) / $BufferSize)
	$Data = FileRead($FileHandle, $BufferSize)
	$RetM = _CRC32($Data, BitNot($CRC32))
Next
FileClose($FileHandle)

EndFunc

В коде используется библиотека CRC.au. Где ее брал уже не помню, поэтому файл выкладываю здесь.
В данном коде проверяется каждые 5 секунд файл "C:\Test.txt". Если файл изменен, выводится сообщение.
 

AZJIO

Меценат
Меценат
Сообщения
2,874
Репутация
1,194
ViktorSPB
Для себя пример сделал. На счёт нагрузки процессора даже незнаю, возможно мониторить дату изменения раз в секунду будет даже экономичней, нагрузка на проц около нуля, просто попробуй любой функцией получить файлы вычислив по таймеру время обработки и далее получить эти же файлы с чтением времени изменений, у меня 14000 файлов за 1 сек. Если 1 файл за сек, то раздели 100% нагруженности проца на 14000, получаем 0,007%
Код:
; Использовал UDF по ссылке, модернизировал для себя (AZJIO)
; http://www.autoitscript.com/forum/index.php?showtopic=113560&st=0


Global $pDirEvents
Global $hDir
Global $pOverLapped
Global $tFNI
Global $pBuffer
Global $Filename
Global $iBufferSize
Global $tOverLapped
Global $tBuffer
Global $tDirEvents
Global $iDirEvents
Global $hEvent
Global $FileMon='проверочный файл.ini'
If Not FileExists($FileMon) Then
	MsgBox(0, 'Сообщение', 'Не найден файл')
	Exit
EndIf

; тело скрипта
;!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i
$Tr1=0
_FileSysMonSetup()
While 1
	$Filename=_FileSysMonDirEventHandler()
	If $Filename=$FileMon Then
		$Tr1+=1
		If $Tr1=2 Then
			MsgBox(0, 'Сообщение', 'Файл изменён')
			$Tr1=0
		EndIf
		$Filename=''
	EndIf
	Sleep(1000)
WEnd

Func _FileSysMonActionEvent($event_value)
	;Двойное сообщение - до и после изменения, напримеро пеерименование.
	MsgBox(0, 'Message', $event_value)
EndFunc   ;==>_FileSysMonActionEvent
;!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i!i

Func _FileSysMonSetup($sdir = @ScriptDir)
		$tBuffer = DllStructCreate("byte[4096]")
		$pBuffer = DllStructGetPtr($tBuffer)
		$iBufferSize = DllStructGetSize($tBuffer)
		$tFNI = 0
		$hDir = DllCall("kernel32.dll", "hwnd", "CreateFile", "Str", $sdir, "Int", 0x1, "Int", BitOR(0x1, 0x4, 0x2), "ptr", 0, "int", 0x3, "int", BitOR(0x2000000, 0x40000000), "int", 0)
		$hDir = $hDir[0]
		$tReadLen = DllStructCreate("dword ReadLen")
		$tOverLapped = DllStructCreate("Uint OL1;Uint OL2; Uint OL3; Uint OL4; hwnd OL5")
		For $i = 1 To 5
			DllStructSetData($tOverLapped, $i, 0)
		Next
		$pOverLapped = DllStructGetPtr($tOverLapped)
		$iOverLappedSize = DllStructGetSize($tOverLapped)
		$tDirEvents = DllStructCreate("hwnd DirEvents")
		$pDirEvents = DllStructGetPtr($tDirEvents)
		$iDirEvents = DllStructGetSize($tDirEvents)
		$hEvent = DllCall("kernel32.dll", "hwnd", "CreateEvent", "UInt", 0, "Int", True, "Int", False, "UInt", 0)
		DllStructSetData($tOverLapped, 5, $hEvent[0])
		DllStructSetData($tDirEvents, 1, $hEvent[0])
		$ret = DllCall("kernel32.dll", "Int", "ReadDirectoryChangesW", "hwnd", $hDir, "ptr", $pBuffer, "dword", $iBufferSize, "int", False, "dword", BitOR(0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x100), "Uint", 0, "Uint", $pOverLapped, "Uint", 0)
		$Filename = ""
	Return True
EndFunc   ;==>_FileSysMonSetup

Func _FileSysMonDirEventHandler()
	Local $r, $iOffset, $nReadLen, $tStr, $iNext, $ff, $ret
	$r = DllCall("User32.dll", "dword", "MsgWaitForMultipleObjectsEx", "dword", 1, "ptr", $pDirEvents, "dword", 100, "dword", 0x4FF, "dword", 0x6)
	If $r[0] = 0 Then
		$iOffset = 0
		$nReadLen = 0
		$d = 0
		DllCall("kernel32.dll", "Uint", "GetOverlappedResult", "hWnd", $hDir, "Uint", $pOverLapped, "UInt*", $nReadLen, "Int", True)
		While 1
			$tFNI = DllStructCreate("dword Next;dword Action;dword FilenameLen", $pBuffer + $iOffset)
			$tStr = DllStructCreate("wchar[" & DllStructGetData($tFNI, "FilenameLen") / 2 & "]", $pBuffer + $iOffset + 12)
			$Filename = DllStructGetData($tStr, 1)
			$iNext = DllStructGetData($tFNI, "Next")
			If $iNext = 0 Then ExitLoop
			$iOffset += $iNext
		WEnd
		$ff = DllStructGetData($tOverLapped, 5)
		DllCall("kernel32.dll", "Uint", "ResetEvent", "UInt", $ff)
		$ret = DllCall("kernel32.dll", "Int", "ReadDirectoryChangesW", "hwnd", $hDir, "ptr", $pBuffer, "dword", $iBufferSize, "int", False, "dword", BitOR(0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x100), "Uint", 0, "Uint", $pOverLapped, "Uint", 0)
	EndIf
	Return $Filename
EndFunc   ;==>_FileSysMonDirEventHandler

тест
Код:
#include <_FileSearch.au3>
$FileList=_FileSearch(@WindowsDir)

$timer = TimerInit()
$q=0
For $i = 1 to $FileList[0]
	FileGetTime($FileList[$i])
	If Not @error Then $q+=1
Next
$timer = Round(TimerDiff($timer) / 1000, 2) & ' сек'
MsgBox(0, 'Сообщение', $q  &@CRLF&  $timer)
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
ViktorSPB [?]
Победил прекращение с кнопки в окне
Выложите решение в этом случае, оно может кому-нибудь пригодиться, ИМХО.
_WinAPI_EmptyWorkingSet я так понимаю отключает что-то не нужное, чтобы систему не загружать? Читал хэлп, но он мне не прояснил ситуацию.
А Вы сравните потребление памяти процессом AutoIt3.exe (если запускаете из SciTE) с подключенной функцией и без нее.
 
Автор
V

ViktorSPB

Новичок
Сообщения
109
Репутация
0
А Вы сравните потребление памяти процессом AutoIt3.exe (если запускаете из SciTE) с подключенной функцией и без нее.
Так потому и спросил, что своими скудными средствами проверил, т.е. диспетчером задач. Разницы не увидел. Ни в нагрузке на проц, ни в расходе памяти. Тем не менее, пользоваться этой функцией буду, и, благодаря Вашему примеру с кодом, я разобрался как подключить библиотеку и к эдитору и к самой среде, теперь буду рассматривать какие еще есть библиотеки.

Теперь тот же скрипт, но с выходом по нажатию на кнопке контекстного меню.
Опять же, понимаю что сделано криво и влоб, учусь я только)
Код:
#include <WinAPIEx.au3>;Yashied, http://autoit-script.ru/index.php/topic,47.0.html
#include <GUIConstants.au3>

Opt('MustDeclareVars', 1)
Opt('TrayMenuMode', 1)

Global $sFile = 'C:\tmp\2.txt', _ ;путь к файлу, за которым будем смотреть;
        $iTimeOut = 500, _ ;5 сек. перерыв между проверками;
        $sTimeModifiedOLd, _ ;дата и время предыдущего изменения файла;
        $sTimeModified, _ ;дата и время изменения файла;
        $iStart
Dim $stop_button,$msg
	;HotKeySet('{Esc}', '_Exit') ;назначаем горячую клавишу (Esc) на выход;
GUICreate("Update control", 200, 150)
GUICtrlCreateLabel("Контроль изменений в"&@LF&$sFile, 30, 10)
$stop_button = GUICtrlCreateButton("Стоп", 50, 100, 100)
GUISetState(@SW_SHOW)


If Not FileExists($sFile) Then ;если файла нет, то выход;
    MsgBox(16, 'Error', 'Отсутствует файл' & @LF & $sFile)
    Exit
EndIf
$sTimeModifiedOLd = FileGetTime($sFile, 0, 1);получаем дату и время изменения файла;
$iStart = TimerInit();запускаем таймер;
_WinAPI_EmptyWorkingSet() ;см. в справке к WinAPIEx.au3;
While 1 ;запускаем бесконечный цикл
	$msg = GUIGetMsg()

	Select
		Case $msg = $stop_button
			_Exit()
	ExitLoop
	EndSelect

	If TimerDiff($iStart) >= $iTimeOut Then ;если прошло 5 сек.;
		If FileExists($sFile) Then ;проверяем, что файл на месте;
			$sTimeModified = FileGetTime($sFile, 0, 1);получаем дату и время изменения файла;
			If $sTimeModifiedOLd <> $sTimeModified Then;если значение изменилось;
				;или делаем то, что нам нужно;
				TrayTip('Test', 'Файл был изменен ' & @LF & @HOUR & ':' & @MIN & ':' & @SEC, 3, 2);выводим сообщение;
				$sTimeModifiedOLd = $sTimeModified ;присваиваем старому значению новое;
			EndIf
		EndIf
		$iStart = TimerInit();запускаем таймер;
	EndIf
	Sleep(100) ;пауза, чтобы не грузить процессор.

WEnd

Func _Exit()
    Exit
EndFunc   ;==>_Exit


Хотел было продублировать в окне время последнего изменения, но когда увидел, что происходит с системой во время прорисовки таблицы в бесконечном цикле, отставил задачу)
Интересно, а может ли быть GUI как бы в фоне, и при изменении какой-либо переменной, отвечающей за внешний вид или наполнение окна, обрабатывает один раз это изменение, и окно больше не перерисовывается? По идее, в скриптовом языке такого быть не должно, но насчет AutoIt не знаю, уж больно он мне пока нравится, может и работает по внутренним прерываниям или событиям)



Добавлено:
Сообщение автоматически объединено:

2 VladUs и AZJIO.
Спасибо вам за ваши варианты решения. К сожалению, они пока далеки от моего понимания, я только разбираюсь .... ну, грубо, с синтаксисом, а тут уже пошли длл-ки) Я обязательно вернусь к вашим вариантам, а сейчас, к сожалению, мне сложно их комментировать. Мне нужно еще одну задачу решить, но она уже в другой ветке будет.
Спасибо за помощь!


Добавлено:
Сообщение автоматически объединено:

Разобрался со второй задачей! :IL_AutoIt_1:
Очень приятная среда AutoIt!
Буду дальше вникать, чувствую, стоит того.
Спасибо всем, кто помог начать разбираться! :beer:
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
ViktorSPB,
Пример с окном.
Код:
#include <WinAPIEx.au3>;Yashied, http://autoit-script.ru/index.php/topic,47.0.html
#include <GUIConstantsEx.au3>
#include <StaticConstants.au3>

Opt('MustDeclareVars', 1)
Opt('TrayMenuMode', 1)

Global $sFile = 'C:\tmp\2.txt', $iTimeOut = 500, $sTimeModifiedOLd, $sTimeModified, _
		$iStart, $nStop_Button, $nLabel_LastChange, $sMessage, $iCount, $nLabel_Count, $fControl = True

If Not FileExists($sFile) Then
	MsgBox(16, 'Error', 'Отсутствует файл' & @LF & $sFile)
	Exit
EndIf
$sTimeModifiedOLd = FileGetTime($sFile, 0, 1)
$sMessage = StringRegExpReplace($sTimeModifiedOLd, '^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$', '$3.$2.$1 $4:$5:$6')
GUICreate('Update control', 300, 150)
GUICtrlCreateLabel('Контроль изменений в' & @LF & $sFile, 5, 10, 290, 30, $SS_CENTER)
$nLabel_LastChange = GUICtrlCreateLabel($sMessage, 70, 50, 110, 15, $SS_CENTER)
$nLabel_Count = GUICtrlCreateLabel('0', 190, 50, 40, 15, $SS_CENTER)
For $i = $nLabel_LastChange - 1 To $nLabel_Count
	GUICtrlSetBkColor($i, 0xFFFFFF)
Next
$nStop_Button = GUICtrlCreateButton('Стоп', 100, 100, 100)
GUISetState()

$iStart = TimerInit()
_WinAPI_EmptyWorkingSet()
While 1
	Switch GUIGetMsg()
		Case $GUI_EVENT_CLOSE
			Exit
		Case $nStop_Button
			$fControl = Not $fControl
			If $fControl Then
				GUICtrlSetData($nStop_Button, 'Стоп')
				For $i = $nLabel_LastChange - 1 To $nLabel_Count
					GUICtrlSetBkColor($i, 0xFFFFFF)
				Next
			Else
				GUICtrlSetData($nStop_Button, 'Старт')
				For $i = $nLabel_LastChange - 1 To $nLabel_Count
					GUICtrlSetBkColor($i, 0xF0F0F0)
				Next
			EndIf
	EndSwitch
	If $fControl Then
		If TimerDiff($iStart) >= $iTimeOut Then
			If FileExists($sFile) Then
				$sTimeModified = FileGetTime($sFile, 0, 1)
				If $sTimeModifiedOLd <> $sTimeModified Then
					_WinAPI_MessageBeep(3)
					$sTimeModifiedOLd = $sTimeModified
					$sMessage = StringRegExpReplace($sTimeModifiedOLd, '^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$', '$3.$2.$1 $4:$5:$6')
					$iCount += 1
					GUICtrlSetData($nLabel_LastChange, $sMessage)
					GUICtrlSetData($nLabel_Count, $iCount)
				EndIf
			EndIf
			$iStart = TimerInit()
		EndIf
	EndIf
WEnd

Используйте для кода тег AutoIt.
 
Автор
V

ViktorSPB

Новичок
Сообщения
109
Репутация
0
madmasles, что я могу сказать? - просто отлично, просто то, что мне нужно. Сейчас я должен другой скрипт дописать, в другой программе, и тогда вернусь и разберусь с Вашим кодом. Все по полочкам разложу для себя) Надо делать все по порядку, во всяком случае стремиться к этому)))
[box title=PS]
насчет оформления кода, понял, спасибо!
[/box]
 
Автор
V

ViktorSPB

Новичок
Сообщения
109
Репутация
0
Исправил. Это я не из-за лени не исправлял, не подумайте там чего)
 

BizSV

Новичок
Сообщения
16
Репутация
0
VladUs сказал(а):
В коде используется библиотека CRC.au. Где ее брал уже не помню, поэтому файл выкладываю здесь.

Дайте , пожалуйста CRC.au3.
Не могу нигде найти :(.
 

BizSV

Новичок
Сообщения
16
Репутация
0
Alofa сказал(а):
Это все в прошлом :smile:

Спасибо :beer:


Добавлено:
Сообщение автоматически объединено:

Alofa сказал(а):
Это все в прошлом :smile:
Код:
_Crypt_HashFile()
_Crypt_HashData()

А еще какие нибудь способы есть? :stars:

Дело в том что у меня около 4 тб файлов надо отслеживать на изменения. Проверка запускается раз в сутки. И по моим подсчетам будет продолжаться более 11 часов. :'(

Можно как то более быстро и пусть менее качественно получить хеш файлов?
 
A

Alofa

Гость
BizSV сказал(а):
как то более быстро и пусть менее качественно получить хеш файлов?
Тогда теряется весь смысл.
В таком случае можете просто проверять дату последнего изменения файлов.
 

Garrett

Модератор
Локальный модератор
Сообщения
3,999
Репутация
967
Верх