Что нового

RDC UDF - Мониторинг папок на предмет изменений

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 714
AutoIt: 3.3.6.1
Версия: 1.0

Категория: Файловая система

Описание: Хочу предложить свой способ использования API ReadDirectoryChangesW в AutoIt. Основная проблема здесь в том, что в полной мере работать с этой функцией без создания потока не получится. Для решения этой проблемы я написал простой DLL, который берет на себя всю работу с потоками. В результате AutoIt скрипту остается только принимать и обрабатывать данные, которые поступают из соответствующего потока. Для того, чтобы оценить все возможности библиотеки, посмотрите полноценный пример с GUI, который находится внутри архива. Для более подробной информации смотрите описание для каждой функции в самой библиотеке.

Ниже приведены основные характеристики библиотеки.
  • Возможность создания нескольких потоков (не лимитировано) для мониторинга разных папок.
  • Поддержка горячего (небезопасного) отключение съемных устройств, таких как USB флэш-диск, и т.д.
  • Поддержка UNC и сетевых дисков.
  • Поддержка 32- и 64-разрядных процессов (RDC.dll и RDC_x64.dll).
  • Простота использования функций библиотеки.
  • Полноценные примеры, включая GUI.

Список функций:
_RDC_CloseDll
_RDC_Create
_RDC_Delete
_RDC_Destroy
_RDC_EnumRDC
_RDC_GetCount
_RDC_GetData
_RDC_GetDirectory
_RDC_GetRDCInfo
_RDC_OpenDll
_RDC_Resume

Файл(ы): RDC.zip (x86 и x64)

Пример1 (режим цикла):
Код:
#Include <APIConstants.au3>
#Include <RDC.au3>

Opt('MustDeclareVars', 1)
Opt('TrayAutoPause', 0)

_RDC_OpenDll()
If @Error Then
	ConsoleWrite('Error: _RDC_OpenDll() - ' & @Error & @CR)
	Exit
EndIf

Global $aDir[3], $ID[3], $aData

For $i = 0 To 2
	$aDir[$i] = @ScriptDir & '\~TEST' & ($i + 1) & '~'
	If Not FileExists($aDir[$i]) Then
		DirCreate($aDir[$i])
	EndIf
Next

For $i = 0 To 2
	$ID[$i] = _RDC_Create($aDir[$i], 1, BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME, $FILE_NOTIFY_CHANGE_SIZE))
	If @Error Then
		ConsoleWrite('Error: _RDC_Create() - ' & @Error & ', ' & @Extended & @CR)
		Exit
	EndIf
Next

While 1
	For $i = 0 To 2
		If $ID[$i] = -1 Then
			ContinueLoop
		EndIf
		$aData = _RDC_GetData($ID[$i])
		If @Error Then

			ConsoleWrite('Error: _RDC_GetData() - ' & @Error & ', ' & @Extended & ', ' & _RDC_GetDirectory($ID[$i]) & @CR)

			; Delete thread to avoid receiving this error!
			_RDC_Delete($ID[$i])
			$ID[$i] = -1
			ContinueLoop
		EndIf
		For $j = 1 To $aData[0][0]
			ConsoleWrite($aData[$j][1] & ' - ' & _RDC_GetDirectory($ID[$i]) & '\' & $aData[$j][0] & @CR)
		Next
	Next
	Sleep(10)
WEnd

Пример2 (режим уведомлений):
Код:
#Include <APIConstants.au3>
#Include <RDC.au3>

Opt('MustDeclareVars', 1)
Opt('TrayAutoPause', 0)

_RDC_OpenDll()
If @Error Then
	ConsoleWrite('Error: _RDC_OpenDll() - ' & @Error & @CR)
	Exit
EndIf

Global $hWnd = GUICreate('')
Global $aDir[3]

For $i = 0 To 2
	$aDir[$i] = @ScriptDir & '\~TEST' & ($i + 1) & '~'
	If Not FileExists($aDir[$i]) Then
		DirCreate($aDir[$i])
	EndIf
Next

GUIRegisterMsg($WM_RDC, 'WM_RDC')

For $i = 0 To 2
	_RDC_Create($aDir[$i], 1, BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME, $FILE_NOTIFY_CHANGE_SIZE), 0, $hWnd)
	If @Error Then
		ConsoleWrite('Error: _RDC_Create() - ' & @Error & ', ' & @Extended & @CR)
		Exit
	EndIf
Next

While 1
	Sleep(1000)
WEnd

Func WM_RDC($hWnd, $iMsg, $wParam, $lParam)

	#forceref $hWnd, $iMsg, $wParam

	Local $aData = _RDC_GetData($lParam)

	If @Error Then

		; Do something because notifications will not come from this thread!
		ConsoleWrite('Error: _RDC_GetData() - ' & @Error & ', ' & @Extended & ', ' & _RDC_GetDirectory($lParam) & @CR)

		_RDC_Delete($lParam)
		Return 0
	EndIf
	For $i = 1 To $aData[0][0]
		ConsoleWrite($aData[$i][1] & ' - ' & _RDC_GetDirectory($lParam) & '\' & $aData[$i][0] & @CR)
	Next
    Return 0
EndFunc   ;==>WM_RDC

Пример3 (режим уведомлений, расширенный):
Код:
#Include <APIConstants.au3>
#Include <RDC.au3>

Opt('MustDeclareVars', 1)
Opt('TrayAutoPause', 0)

Global Const $sDir = @ScriptDir & '\~TEST~'

_RDC_OpenDll()
If @Error Then
	ConsoleWrite('Error: _RDC_OpenDll() - ' & @Error & @CR)
	Exit
EndIf

Global $hWnd = GUICreate('')
Global $sEvents = ''

If Not FileExists($sDir) Then
	DirCreate($sDir)
EndIf

GUIRegisterMsg($WM_RDC, 'WM_RDC')

_RDC_Create($sDir, 1, BitOR($FILE_NOTIFY_CHANGE_FILE_NAME, $FILE_NOTIFY_CHANGE_DIR_NAME, $FILE_NOTIFY_CHANGE_SIZE), 0, $hWnd)
If @Error Then
	ConsoleWrite('Error: _RDC_Create() - ' & @Error & ', ' & @Extended & @CR)
	Exit
EndIf

While 1
	Sleep(1000)
WEnd

Func _IsDirectory($sPath)
	If StringInStr(FileGetAttrib($sPath), 'D') Then
		Return 1
	Else
		Return 0
	EndIf
EndFunc   ;==>_IsDirectory

Func _RetrieveDirectoryChanges()

	AdlibUnRegister('_RetrieveDirectoryChanges')

	Local $aData, $aText, $aPrev[2] = [0, ''], $sPrev = ''

	$aData = StringSplit($sEvents, '|', 2)
	$sEvents = ''
	If Not IsArray($aData) Then
		Return
	EndIf
	For $i = 0 To UBound($aData)
		If $i < UBound($aData) Then
			If $aData[$i] = $sPrev Then
				ContinueLoop
			EndIf
			$sPrev = $aData[$i]
			$aText = StringSplit($aData[$i], '?', 2)
			If IsArray($aText) Then
;~				ConsoleWrite($aText[0] & ' - ' & $aText[1] & @CR)
;~				ContinueLoop
				Switch Number($aText[0])
					Case 1 ; FILE_ACTION_ADDED
						Switch Number($aPrev[0])
							Case 2
								If StringRegExpReplace($aPrev[1], '^.*\\', '') = StringRegExpReplace($aText[1], '^.*\\', '') Then
									If $aPrev[1] = $aText[1] Then
;~										If _IsDirectory($aText[1]) Then
;~											; Nothing
;~										Else
;~											; Nothing
;~										EndIf
										$aPrev[0] = 0
										ContinueLoop
									Else
										If _IsDirectory($aText[1]) Then
											ConsoleWrite('DIRECTORY MOVED: ' & $aPrev[1] & ' ---> ' & $aText[1] & @CR)
										Else
											ConsoleWrite('FILE MOVED: ' & $aPrev[1] & ' ---> ' & $aText[1] & @CR)
										EndIf
										$aPrev[0] = 0
										ContinueLoop
									EndIf
								EndIf
						EndSwitch
					Case 2 ; FILE_ACTION_REMOVED
						; Nothing
					Case 3 ; FILE_ACTION_MODIFIED
						Switch Number($aPrev[0])
							Case 1
;~								If True Then
									If $aPrev[1] = $aText[1] Then
										If _IsDirectory($aText[1]) Then
											; Nothing
										Else
											ConsoleWrite('FILE ADDED: ' & $aText[1] & @CR)
										EndIf
										$aPrev[0] = 0
										ContinueLoop
									EndIf
;~								EndIf
							Case 2
								If StringRegExpReplace($aPrev[1], '^.*\\', '') = StringRegExpReplace($aText[1], '^.*\\', '') Then
;~									If True Then
										If _IsDirectory($aText[1]) Then
											; Nothing
										Else
											ConsoleWrite('FILE DELETED: ' & $aText[1] & @CR)
											ConsoleWrite('FILE MOVED: ' & $aPrev[1] & ' ---> ' & $aText[1] & @CR)
										EndIf
										$aPrev[0] = 0
										ContinueLoop
;~									EndIf
								EndIf
						EndSwitch
					Case 4 ; FILE_ACTION_RENAMED_OLD_NAME
						; Nothing
					Case 5 ; FILE_ACTION_RENAMED_NEW_NAME
						Switch Number($aPrev[0])
							Case 4
								If StringRegExpReplace($aPrev[1], '\\[^\\]*\Z', '') = StringRegExpReplace($aText[1], '\\[^\\]*\Z', '') Then
;~									If True Then
										If _IsDirectory($aText[1]) Then
											ConsoleWrite('DIRECTORY RENAMED: ' & $aPrev[1] & ' ---> ' & $aText[1] & @CR)
										Else
											ConsoleWrite('FILE RENAMED: ' & $aPrev[1] & ' ---> ' & $aText[1] & @CR)
										EndIf
										$aPrev[0] = 0
										ContinueLoop
;~									EndIf
								EndIf
						EndSwitch
				EndSwitch
			EndIf
		EndIf
		Switch Number($aPrev[0])
			Case 1 ; FILE_ACTION_ADDED
				If _IsDirectory($aPrev[1]) Then
					ConsoleWrite('DIRECTORY ADDED: ' & $aPrev[1] & @CR)
				Else
					ConsoleWrite('FILE ADDED: ' & $aPrev[1] & @CR)
				EndIf
			Case 2 ; FILE_ACTION_REMOVED
;~				If True Then
					ConsoleWrite('FILE OR DIRECTORY DELETED: ' & $aPrev[1] & @CR)
;~				EndIf
			Case 3 ; FILE_ACTION_MODIFIED
				If _IsDirectory($aPrev[1]) Then
					; Nothing
				Else
					ConsoleWrite('FILE MODIFIED: ' & $aPrev[1] & @CR)
				EndIf
		EndSwitch
		$aPrev = $aText
	Next
	ConsoleWrite('---------------------------------------------' & @CR)
EndFunc   ;==>_RetrieveDirectoryChanges

Func WM_RDC($hWnd, $iMsg, $wParam, $lParam)

	#forceref $hWnd, $iMsg, $wParam

	Local $aData = _RDC_GetData($lParam)

	If @Error Then

		; Do something because notifications will not come from this thread!
		ConsoleWrite('Error: _RDC_GetData() - ' & @Error & ', ' & @Extended & ', ' & _RDC_GetDirectory($lParam) & @CR)

		_RDC_Delete($lParam)
		Return 0
	EndIf
	For $i = 1 To $aData[0][0]
		If $sEvents Then
			$sEvents &= '|'
		EndIf
		$sEvents &= $aData[$i][1] & '?' & _RDC_GetDirectory($lParam) & '\' & $aData[$i][0]
	Next
	AdlibRegister('_RetrieveDirectoryChanges', 250)
    Return 0
EndFunc   ;==>WM_RDC

Пример4 (GUI):
См. GUI.zip внутри архива.

Скриншот:


Источник: RDC UDF (официальный форум)
Автор: Yashied
 

inververs

AutoIT Гуру
Сообщения
2 135
Репутация
464
Суппер! А на каком языке dll? можно посмотреть исходник?
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 714
inververs сказал(а):
А на каком языке dll?
PB 4.50

Код:
#RDC_WIN_ID = "A0397257-BD4F-41BB-914D-4E3923332BB9"

Structure Params
    *Buffer
    *Directory
    *Window
    WM.l
    Subtree.l
    Filter.l
    ID.l
    *WP
EndStructure

Structure Buffer
    *Raw
    Length.l
    Ready.l
    Wait.l
    Error.l
EndStructure

Structure DBH
    Size.l
    DeviceType.l
    Reserved.l
EndStructure

Structure DBV Extends DBH
    Mask.l
    Flags.w
EndStructure

Global Dim Thread(100, 4)

; (i, 0) - Thread ID
; (i, 1) - Pointer to the "Params" memory
; (i, 2) - Handle to the directory
; (i, 3) - Drive number ((-1), 0-25)

Import "kernel32.lib"
    GetVolumePathNameW.l(FileName, VolumePathName, BufferLength.l)
    ReadDirectoryChangesW.l(Directory, Buffer, BufferLength.l, WatchSubtree.l, NotifyFilter.l, BytesReturned, Overlapped, CompletionRoutine)
EndImport

Prototype.l GetFinalPathNameByHandleW(File, FilePath, FilePathLength.l, Flags.l)

Procedure.l GetDriveNumber(Path)
    Drive.s{1024}
    If GetVolumePathNameW(Path, @Drive, 1024)
        ProcedureReturn PathGetDriveNumber_(@Drive)
    Else
        ProcedureReturn -1
    EndIf
EndProcedure

Procedure.l GetDriveNumberByHandle(File)
    If Not OpenLibrary(0, "kernel32.dll")
        ProcedureReturn -1
    EndIf
    Path.s{4096}
    GetFinalPathNameByHandleW.GetFinalPathNameByHandleW = GetFunction(0, "GetFinalPathNameByHandleW")
    If GetFinalPathNameByHandleW(File, @Path, 4096 - 1, 0)
        Drive.l = PathGetDriveNumber_(@Path)
    Else
        Drive.l = -1
    EndIf
    CloseLibrary(0)
    ProcedureReturn Drive
EndProcedure

Procedure ReadDirectory(*Params.Params)
    *Buffer.Buffer = *Params\Buffer
    *Buffer\Ready = 0
    *Buffer\Error = 0
    While *Buffer\Wait
        Delay(10)
    Wend
    Length = 0
    Repeat
        Result = ReadDirectoryChangesW(*Params\Directory, *Buffer\Raw, *Buffer\Length, *Params\Subtree, *Params\Filter, @Length, #Null, #Null)
        If (Not Result) Or (Not Length)
            Error = GetLastError_()
            Select Error
                Case #ERROR_BAD_NET_RESP
                    ; Nothing
                Default
                    If Error
                        *Buffer\Error = Error
                    Else
                        *Buffer\Error = #ERROR_NOTIFY_ENUM_DIR
                    EndIf
                    PostMessage_(*Params\Window, *Params\WM, *Params\WP, *Params\ID)
                    Break
            EndSelect
        EndIf
        *Buffer\Ready = 1
        If *Params\Window
            PostMessage_(*Params\Window, *Params\WM, *Params\WP, *Params\ID)
        EndIf
        While *Buffer\Ready
            ; Nothing
        Wend
    ForEver
    Repeat
        Delay(1000)
    ForEver
EndProcedure

Procedure WinCallback(hWnd, uMsg, wParam, lParam)
    Select uMsg
        Case #WM_DEVICECHANGE
            Select wParam
                Case #DBT_DEVICEREMOVECOMPLETE
                    *DBH.DBH = lParam
                    Select *DBH\DeviceType
                        Case #DBT_DEVTYP_VOLUME
                            *DBV.DBV = lParam
                            For i = 0 To 25
                                If (*DBV\Mask >> i) & 1
                                    For j = 0 To ArraySize(Thread()) - 1
                                        If (Thread(j, 0)) And (Thread(j, 3) = i)
                                            If Not KillThread(Thread(j, 0))
                                                ; Nothing
                                            EndIf
                                            *Params.Params = Thread(j, 1)
                                            *Params\Directory = 0
                                            CloseHandle_(Thread(j, 2))
                                            Thread(j, 0) = CreateThread(@ReadDirectory(), Thread(j, 1))
                                            Thread(j, 2) = 0
                                            Thread(j, 3) =-1
                                            If Not Thread(j, 0)
                                                ; Nothing
                                            EndIf
                                        EndIf
                                    Next
                                    Break
                                EndIf
                            Next
                    EndSelect
            EndSelect
    EndSelect
    ProcedureReturn #PB_ProcessPureBasicEvents 
EndProcedure

DeclareDLL.l RDC_Delete(ID.l)

ProcedureDLL AttachProcess(Instance)
    If OpenWindow(0, #PB_Ignore, #PB_Ignore, #PB_Ignore, #PB_Ignore, #RDC_WIN_ID, #PB_Window_Invisible)
        SetWindowCallback(@WinCallback())
    EndIf
EndProcedure

ProcedureDLL DetachProcess(Instance)
    For i = 0 To ArraySize(Thread()) - 1
        RDC_Delete(i)
    Next
    SetWindowCallback(0)
    CloseWindow(0)
EndProcedure

ProcedureDLL.l RDC_Create(Path, Subtree.l, Filter.l, Buffer, Window, WM.l, WP)
    hDir = CreateFile_(Path, #FILE_LIST_DIRECTORY, #FILE_SHARE_READ | #FILE_SHARE_WRITE, #Null, #OPEN_EXISTING, #FILE_FLAG_BACKUP_SEMANTICS, #Null)
    If Not hDir
        ProcedureReturn -1
    EndIf
    ID = 0
    Length = ArraySize(Thread())
    While 1
        If ID = Length
            ReDim Thread(Length + 100, ArraySize(Thread(), 2))
            Break
        EndIf
        If Not Thread(ID, 0)
            Break
        EndIf
        ID + 1
    Wend
    *Params.Params = AllocateMemory(SizeOf(Params))
    *Params\Buffer = Buffer
    *Params\Directory = hDir
    *Params\Window = Window
    *Params\WM = WM
    *Params\Subtree = Subtree
    *Params\Filter = Filter
    *Params\ID = ID
    *Params\WP = WP
    Thread(ID, 0) = CreateThread(@ReadDirectory(), *Params)
    Thread(ID, 1) = *Params
    Thread(ID, 2) = hDir
;   If OSVersion() >= #PB_OS_Windows_Vista
;       Thread(ID, 3) = GetDriveNumberByHandle(hDir)
;   Else
        Thread(ID, 3) = GetDriveNumber(Path)
;   EndIf
    If Not Thread(ID, 0)
        RDC_Delete(ID)
        ProcedureReturn -1
    EndIf
    ProcedureReturn ID
EndProcedure

ProcedureDLL.l RDC_Delete(ID.l)
    If (ID < 0) Or (ID > ArraySize(Thread())) Or (Not Thread(ID, 0))
        ProcedureReturn 0
    EndIf
    KillThread(Thread(ID, 0))
    FreeMemory(Thread(ID, 1))
    If Thread(ID, 2)
        CloseHandle_(Thread(ID, 2))
    EndIf
    Thread(ID, 0) = 0
    ProcedureReturn 1
EndProcedure
 

gloss

Ленивое кодило
Сообщения
155
Репутация
5
Можно ли установить исключения для некоторых папок?
В качестве папки выбираем диск С и лезем в интернет с любого браузера и получается такое чудо:
Было бы очень кстати пропускать все, что происходит в C:\Users\Doctor\AppData\Roaming\Opera Software
Спасибо.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 552
Репутация
2 429
gloss [?]
Можно ли установить исключения для некоторых папок?
Их можно фильтровать на выходе, типа:
Код:
If _RDC_GetDirectory($ID[$i]) <> @AppDataDir & '\Opera Software' Then
;Заносим папку в список
EndIf
 

gloss

Ленивое кодило
Сообщения
155
Репутация
5
Эхх.. Моих знаний не хватает чтобы додумать куда это пристроить и что еще нужно дописать. Нет понимания как это работает. :(
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 552
Репутация
2 429
gloss [?]
Нет понимания как это работает
В примере с GUI найти:
Код:
Global Const $GUI_NAME = 'RDC UDF Example'

заменить на:
Код:
Global Const $aExcludes = StringSplit(@ProgramFilesDir & '|' & @AppDataDir & '\Opera Software', '|')
Global Const $GUI_NAME = 'RDC UDF Example'

далее найти:
Код:
For $i = 1 To $aData[0][0]
					$LV[$ID][6] &= $aData[$i][1] & '*\' & $aData[$i][0] & '*' & _WinAPI_GetDateFormat() & ' ' & _WinAPI_GetTimeFormat(0, 0, $TIME_NOSECONDS) & '|'
				Next

и заменить на:
Код:
Local $sRoot = _RDC_GetDirectory($lParam)
				
				For $i = 1 To $aData[0][0]
					For $j = 1 To $aExcludes[0]
						If $sRoot & $aData[$i][0] = $aExcludes[$j] Or StringInStr($sRoot & $aData[$i][0], $aExcludes[$j] & '\', 2) Then
							Return 0
						EndIf
					Next
					
					$LV[$ID][6] &= $aData[$i][1] & '*\' & $aData[$i][0] & '*' & _WinAPI_GetDateFormat() & ' ' & _WinAPI_GetTimeFormat(0, 0, $TIME_NOSECONDS) & '|'
				Next


Список исключений указывается в первой строчке замены.
 

gloss

Ленивое кодило
Сообщения
155
Репутация
5
CreatoR
Долго не мог понять как Список исключений указывается в первой строчке замены.
Все же дошло :smile: И еще раз большое спасибо!
Код:
Global Const $aExcludes = StringSplit(@LocalAppDataDir & '|' & @AppDataDir & '\Opera Software' & '|' & @CRLF & _
										@LocalAppDataDir & '|' & @AppDataDir & '\Notepad++', '|')
 

Vlasssov

Осваивающий
Сообщения
430
Репутация
25
Вопрос: а пользователей, которые добавили, удалили или произвели модификацию файлов, как-то отловить можно?
 

SlavaS

Знающий
Сообщения
35
Репутация
5
Тоже очень интересует данный вопрос. :smile:
 
Автор
Yashied

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 714
Друзья, я предоставил UDF, а не программу. Вопрос про пользователей выходит за рамки данной темы. В любом случае пользователей отследить можно...
 

Vlasssov

Осваивающий
Сообщения
430
Репутация
25
Yashied сказал(а):
Друзья, я предоставил UDF, а не программу. Вопрос про пользователей выходит за рамки данной темы. В любом случае пользователей отследить можно...
Может отдельную тему, обсудить... :laugh:
 

pirpitum

Новичок
Сообщения
7
Репутация
0
здравствуйте, написал с помощью библиотеки программу для отслеживания изменений в текстовом файле в расшаренной папке, пока она была расшарена на 2003 сервере проблем не было. После замены на 2008 сервер перестало работать, отслеживание "засыпает". Если файл открыть блокнотом продолжает какое-то время работать.
 

DyadyaGenya

Новичок
Сообщения
180
Репутация
0
Возможность создания нескольких потоков (не лимитировано) для мониторинга разных папок
Не совсем понятно как закончить мониторить папки. Видимо нужно использовать:
Код:
_RDC_Delete()
    _RDC_Destroy()

Но как?
Если использовать ваш пример с расширенными уведомлениями, то я могу прервать весь скрипт используя Exit или прерывание процесса Autoit. Но хотелось бы прервать только наблюдение за папкой.
 

joiner

Модератор
Локальный модератор
Сообщения
3 372
Репутация
589
_RDC_Delete - для удаления потока.
_RDC_Destroy() - для удаления всех потоков.
в архиве есть пример (папка Gui)
 

joiner

Модератор
Локальный модератор
Сообщения
3 372
Репутация
589
в примере можно добавить папку слежения и удалить ее. в цикле опроса GUI используется функция _RDC_Delete для удаления папки.
по типу примера можно создать свой код.
конкретно в примере функция
Код:
_LV_Delete($iID)
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 552
Репутация
2 429
я могу прервать весь скрипт используя Exit
Мне до сих пор непонятно почему у тебя такое стремление довести всё до крайности? :scratch:
Есть же примеры, справка...


пока понял, что их и нужно использовать для этих целей, но как сделать, не понял
Что именно не понял?
 

DyadyaGenya

Новичок
Сообщения
180
Репутация
0
Мне до сих пор непонятно почему у тебя такое стремление довести всё до крайности?
Может потому что не все понимаю, так как не очень умный и учу много всего сразу и наскоками, а "крайности" помогают некоторому понятию? ))) Но в данной ситуации это только демонстрация того, что смог освоить ((((
Что именно не понял?
Наверное все ((( Заново пробовать скорее всего буду уже через 1-2 недели, после сессии. Сейчас в голове даже ничего не осталось, нужно заново смотреть, что там было не понятно.
конкретно в примере функция
Уже позже попробую разобраться.
 
Верх